GeoJSON
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
Installation
Add the dependency to your shard.yml:
dependencies:
geojson:
github: geocrystal/geojson
and run shards install
require "geojson"
Position
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
Pointgeometry - an array of positions in the case of a
LineStringorMultiPointgeometry - an array of
LineStringor linear ring coordinates in the case of aPolygonorMultiLineStringgeometry - an array of
Polygoncoordinates in the case of aMultiPolygongeometry
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)
GeoJSON types
Point
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
MultiPoint
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.to_json
{
"type":"MultiPoint",
"coordinates":[[100.0, 0.0], [101.0, 1.0]]
}
LineString
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]]
}
MultiLineString
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]
]
]
}
Polygon
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
LineStringwith 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, thecoordinatesmember must be an array of linear ring coordinate arrays. - For
Polygonwith 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]
]
]
}
MultiPolygon
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]
]
]
]
}
GeometryCollection
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]
]
]
}
]
}
Feature
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
Featureobject has a"type"member with the value"Feature". - A
Featureobject 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 theFeatureis unlocated, a JSONnullvalue. - A
Featureobject has a member with the name"properties". The value of the properties member is an object (any JSON object or a JSONnullvalue). - If a
Featurehas a commonly used identifier, that identifier should be included as a member of theFeatureobject 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
}
FeatureCollection
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"
}
]
}
Foreign Members
For example, in the Point 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
Contributing
- 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
Contributors
- Anton Maminov - creator and maintainer
License
Copyright: 2020-2024 Anton Maminov ([email protected])
This library is distributed under the MIT license. Please see the LICENSE file.