GeoJSON

Crystal CI Docs License

Crystal library for reading and writing GeoJSON

This library contains:

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:

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:

The Polygon geometry type definition as follows:

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.

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

  1. Fork it (https://github.com/geocrystal/geojson/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

License

Copyright: 2020-2022 Anton Maminov (anton.maminov@gmail.com)

This library is distributed under the MIT license. Please see the LICENSE file.