Crystal library for reading and writing GeoJSON
This library contains:
- Functions for encoding and decoding GeoJSON formatted data
- Classes for all GeoJSON Objects
- Allow "foreign members" in a GeoJSON Objects
Add the dependency to your shard.yml
:
dependencies:
geojson:
github: geocrystal/geojson
and run shards install
require "geojson"
A position is the fundamental geometry construct. The coordinates
member of a Geometry object is composed of either:
- one position in the case of a
Point
geometry - an array of positions in the case of a
LineString
orMultiPoint
geometry - an array of
LineString
or linear ring coordinates in the case of aPolygon
orMultiLineString
geometry - an array of
Polygon
coordinates in the case of aMultiPolygon
geometry
A position is an array of Float64
.
There must be two or more elements. The first two elements are longitude
and latitude
. Altitude
may be included as an optional third element.
postition = [-80.1347334, 25.7663562, 0.0]
point = GeoJSON::Point.new(position)
A GeoJSON point looks like this:
{
"type": "Point",
"coordinates": [-80.1347334, 25.7663562]
}
It is important to note that coordinates is in the format [longitude, latitude]
.
Longitude comes before latitude in GeoJSON.
For type Point
, the coordinates
member is a single position.
Serialize geometry type:
point = GeoJSON::Point.new([-80.1347334, 25.7663562])
json = point.to_json
# => {"type":"Point","coordinates":[-80.1347334,25.7663562]}
Deserialize geometry type:
point = GeoJSON::Point.from_json(json)
# => #<GeoJSON::Point:0x7f1444af9920>
point.longitude
# => -80.1347334
point.latitude
# => 25.7663562
For type MultiPoint
, the coordinates
member is an array of positions.
point1 = GeoJSON::Point.new(longitude: 100.0, latitude: 0.0)
point2 = GeoJSON::Point.new(longitude: 101.0, latitude: 1.0)
multi_point = GeoJSON::MultiPoint.new([point1, point2])
multi_point.json
{
"type":"MultiPoint",
"coordinates":[[100.0, 0.0], [101.0, 1.0]]
}
For type LineString
, the coordinates
member is an array of two or more positions.
line_string = GeoJSON::LineString.new [[-124.2, 42.0], [-120.0, 42.0]]
line_string.to_json
{
"type": "LineString",
"coordinates": [[-124.2, 42.0], [-120.0, 42.0]]
}
For type MultiLineString
, the coordinates
member is an array of LineString
coordinate arrays.
line_string1 = GeoJSON::LineString.new([[100.0, 0.0], [101.0, 1.0]])
line_string2 = GeoJSON::LineString.new([[102.0, 2.0], [103.0, 3.0]])
multi_line_string = GeoJSON::MultiLineString.new([line_string1, line_string2])
multi_line_string = GeoJSON::MultiLineString.new([
[[100.0, 0.0], [101.0, 1.0]],
[[102.0, 2.0], [103.0, 3.0]],
])
multi_line_string.to_json
{
"type":"MultiLineString",
"coordinates":[
[
[100.0, 0.0],
[101.0, 1.0]
],
[
[102.0, 2.0],
[103.0, 3.0]
]
]
}
GeoJSON polygons represent closed shapes on a map, like triangles, squares, dodecagons, or any shape with a fixed number of sides.
To specify a constraint specific to Polygon
, it is useful to introduce the concept of a linear ring:
- A linear ring is a closed
LineString
with four or more positions. - The first and last positions are equivalent, and they must contain identical values; their representation should also be identical.
- A linear ring is the boundary of a surface or the boundary of a hole in a surface.
- A linear ring must follow the right-hand rule with respect to the area it bounds, i.e., exterior rings are counterclockwise, and holes are clockwise.
The Polygon
geometry type definition as follows:
- For type
Polygon
, thecoordinates
member must be an array of linear ring coordinate arrays. - For
Polygon
with more than one of these rings, the first must be the exterior ring, and any others must be interior rings. The exterior ring bounds the surface, and the interior rings (if present) bound holes within the surface.
polygon = GeoJSON::Polygon.new([
[[-10.0, -10.0], [10.0, -10.0], [10.0, 10.0], [-10.0,-10.0]],
[[-1.0, -2.0], [3.0, -2.0], [3.0, 2.0], [-1.0,-2.0]]
])
polygon.to_json
{
"type": "Polygon",
"coordinates": [
[
[-10.0, -10.0],
[10.0, -10.0],
[10.0,10.0],
[-10.0,-10.0]
],
[
[-1.0, -2.0],
[3.0, -2.0],
[3.0, 2.0],
[-1.0,-2.0]
]
]
}
For type MultiPolygon
, the coordinates
member is an array of Polygon
coordinate arrays.
polygon1 = GeoJSON::Polygon.new(
[[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]]
)
polygon2 = GeoJSON::Polygon.new(
[[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]
)
multi_polygon = GeoJSON::MultiPolygon.new([polygon1, polygon2])
multi_polygon.to_json
{
"type":"MultiPolygon",
"coordinates":[
[
[
[102.0,2.0],
[103.0,2.0],
[103.0,3.0],
[102.0,3.0],
[102.0,2.0]
]
],
[
[
[100.0,0.0],
[101.0,0.0],
[101.0,1.0],
[100.0,1.0],
[100.0,0.0]
]
]
]
}
A GeoJSON object with type GeometryCollection
is a Geometry object.
A GeometryCollection
has a member with the name geometries
. The
value of geometries
is an array. Each element of this array is a
GeoJSON Geometry object.
point = GeoJSON::Point.new([100.0, 0.0])
line_string = GeoJSON::LineString.new([
[101.0, 0.0],
[102.0, 1.0],
])
polygon = GeoJSON::Polygon.new([
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
])
geometry_collection = GeoJSON::GeometryCollection.new([point, line_string, polygon])
geometry_collection.to_json
{
"type":"GeometryCollection",
"geometries":[
{
"type":"Point",
"coordinates":[100.0,0.0]
},
{
"type":"LineString",
"coordinates":[
[101.0,0.0],
[102.0,1.0]
]
},
{
"type":"Polygon",
"coordinates":[
[
[100.0,0.0],
[101.0,0.0],
[101.0,1.0],
[100.0,1.0],
[100.0,0.0]
]
]
}
]
}
A Feature
object represents a spatially bounded thing. Every Feature
object is a GeoJSON object no matter where it occurs in a GeoJSON text.
- A
Feature
object has a"type"
member with the value"Feature"
. - A
Feature
object has a member with the name"geometry"
. The value of the geometry member shall be either a Geometry object as defined above or, in the case that theFeature
is unlocated, a JSONnull
value. - A
Feature
object has a member with the name"properties"
. The value of the properties member is an object (any JSON object or a JSONnull
value). - If a
Feature
has a commonly used identifier, that identifier should be included as a member of theFeature
object with the name"id"
, and the value of this member is either a JSON string or number.
point = GeoJSON::Point.new([-80.1347334, 25.7663562])
properties = {"color" => "red"} of String => JSON::Any::Type
feature = GeoJSON::Feature.new(point, properties, id: 1)
feature.to_json
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[-80.1347334,25.7663562]
},
"properties":{
"color":"red"
},
"id":1
}
A GeoJSON object with the type "FeatureCollection"
is a FeatureCollection
object. A FeatureCollection
object has a member with the name "features"
. The value of "features"
is a JSON array. Each element of the array is a Feature
object. It is possible for this array to be empty.
feature1 = GeoJSON::Feature.new(
GeoJSON::Point.new([102.0, 0.5]),
id: "point"
)
feature2 = GeoJSON::Feature.new(
GeoJSON::Polygon.new([
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
]),
type: "polygon"
)
feature_collection = GeoJSON::FeatureCollection.new([feature1, feature2])
feature_collection.to_json
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[102.0,0.5]
},
"properties":null,
"id":"point"
},
{
"type":"Feature",
"geometry":{
"type":"Polygon",
"coordinates":[
[
[100.0,0.0],
[101.0,0.0],
[101.0,1.0],
[100.0,1.0],
[100.0,0.0]
]
]
},
"properties":null,
"id":"polygon"
}
]
}
For example, in the Pont
object shown below
{
"type": "Point",
"coordinates": [-80.1347334, 25.7663562],
"title": "Example Point"
}
the name/value pair of "title": "Example Point"
is a foreign member.
GeoJSON semantics do not apply to foreign members and their descendants, regardless of their names and values.
If the GeoJSON type include foreign members, this properties in the JSON document will be stored in a Hash(String, JSON::Any)
.
On serialization, any keys inside json_unmapped
will be serialized and appended to the current json object.
point = GeoJSON::Point.new([-80.1347334, 25.7663562])
json_unmapped = Hash(String, JSON::Any).new
json_unmapped["title"] = JSON::Any.new("Example Point")
point.json_unmapped = json_unmapped
- Fork it (https://github.com/geocrystal/geojson/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
- Anton Maminov - creator and maintainer
Copyright: 2020-2021 Anton Maminov ([email protected])
This library is distributed under the MIT license. Please see the LICENSE file.