{"id":19944977,"url":"https://github.com/geocrystal/geojson","last_synced_at":"2025-06-12T03:09:12.136Z","repository":{"id":51607739,"uuid":"243804146","full_name":"geocrystal/geojson","owner":"geocrystal","description":"Crystal library for reading and writing GeoJSON","archived":false,"fork":false,"pushed_at":"2024-03-31T15:48:34.000Z","size":133,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-03T16:34:18.300Z","etag":null,"topics":["crystal","geojson","json","rfc-7946"],"latest_commit_sha":null,"homepage":"","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/geocrystal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-28T16:16:32.000Z","updated_at":"2024-05-17T16:50:04.000Z","dependencies_parsed_at":"2024-03-31T16:45:46.582Z","dependency_job_id":null,"html_url":"https://github.com/geocrystal/geojson","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/geocrystal/geojson","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeojson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeojson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeojson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeojson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/geocrystal","download_url":"https://codeload.github.com/geocrystal/geojson/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeojson/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259388073,"owners_count":22849752,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crystal","geojson","json","rfc-7946"],"created_at":"2024-11-13T00:23:40.965Z","updated_at":"2025-06-12T03:09:12.100Z","avatar_url":"https://github.com/geocrystal.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GeoJSON\n\n[![Crystal CI](https://github.com/geocrystal/geojson/actions/workflows/crystal.yml/badge.svg)](https://github.com/geocrystal/geojson/actions/workflows/crystal.yml)\n[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://geocrystal.github.io/geojson/)\n[![License](https://img.shields.io/github/license/geocrystal/geojson.svg)](https://github.com/geocrystal/geojson/blob/master/LICENSE)\n\nCrystal library for reading and writing [GeoJSON](https://tools.ietf.org/html/rfc7946)\n\nThis library contains:\n\n- Functions for encoding and decoding GeoJSON formatted data\n- Classes for all GeoJSON Objects\n- Allow \"foreign members\" in a GeoJSON Objects\n\n## Installation\n\nAdd the dependency to your `shard.yml`:\n\n```yaml\ndependencies:\n  geojson:\n    github: geocrystal/geojson\n```\n\nand run `shards install`\n\n```crystal\nrequire \"geojson\"\n```\n\n## Position\n\nA position is the fundamental geometry construct.  The `coordinates` member of a Geometry object is composed of either:\n\n- one position in the case of a `Point` geometry\n- an array of positions in the case of a `LineString` or `MultiPoint` geometry\n- an array of `LineString` or linear ring coordinates in the case of a `Polygon` or `MultiLineString` geometry\n- an array of `Polygon` coordinates in the case of a `MultiPolygon` geometry\n\nA position is an array of `Float64`.\n\nThere __must__ be two or more elements. The first two elements are `longitude` and `latitude`. `Altitude` __may__ be included as an optional third element.\n\n```crystal\npostition = [-80.1347334, 25.7663562, 0.0]\npoint = GeoJSON::Point.new(position)\n```\n\n## GeoJSON types\n\n### Point\n\nA GeoJSON point looks like this:\n\n```json\n{\n  \"type\": \"Point\",\n  \"coordinates\": [-80.1347334, 25.7663562]\n}\n```\n\nIt is important to note that coordinates is in the format `[longitude, latitude]`.\nLongitude comes before latitude in GeoJSON.\n\nFor type `Point`, the `coordinates` member is a single position.\n\nSerialize geometry type:\n\n```crystal\npoint = GeoJSON::Point.new([-80.1347334, 25.7663562])\njson = point.to_json\n# =\u003e {\"type\":\"Point\",\"coordinates\":[-80.1347334,25.7663562]}\n```\n\nDeserialize geometry type:\n\n```crystal\npoint = GeoJSON::Point.from_json(json)\n# =\u003e #\u003cGeoJSON::Point:0x7f1444af9920\u003e\npoint.longitude\n# =\u003e -80.1347334\npoint.latitude\n# =\u003e 25.7663562\n```\n\n### MultiPoint\n\nFor type `MultiPoint`, the `coordinates` member is an array of positions.\n\n```crystal\npoint1 = GeoJSON::Point.new(longitude: 100.0, latitude: 0.0)\npoint2 = GeoJSON::Point.new(longitude: 101.0, latitude: 1.0)\n\nmulti_point = GeoJSON::MultiPoint.new([point1, point2])\nmulti_point.to_json\n```\n\n```json\n{\n  \"type\":\"MultiPoint\",\n  \"coordinates\":[[100.0, 0.0], [101.0, 1.0]]\n}\n```\n\n### LineString\n\nFor type `LineString`, the `coordinates` member is an array of two or more positions.\n\n```crystal\nline_string = GeoJSON::LineString.new [[-124.2, 42.0], [-120.0, 42.0]]\nline_string.to_json\n```\n\n```json\n{\n  \"type\": \"LineString\",\n  \"coordinates\": [[-124.2, 42.0], [-120.0, 42.0]]\n}\n```\n\n### MultiLineString\n\nFor type `MultiLineString`, the `coordinates` member is an array of `LineString` coordinate arrays.\n\n```crystal\nline_string1 = GeoJSON::LineString.new([[100.0, 0.0], [101.0, 1.0]])\nline_string2 = GeoJSON::LineString.new([[102.0, 2.0], [103.0, 3.0]])\n\nmulti_line_string = GeoJSON::MultiLineString.new([line_string1, line_string2])\n```\n\n```crystal\nmulti_line_string = GeoJSON::MultiLineString.new([\n  [[100.0, 0.0], [101.0, 1.0]],\n  [[102.0, 2.0], [103.0, 3.0]],\n])\nmulti_line_string.to_json\n```\n\n```json\n{\n  \"type\":\"MultiLineString\",\n  \"coordinates\":[\n    [\n      [100.0, 0.0],\n      [101.0, 1.0]\n    ],\n    [\n      [102.0, 2.0],\n      [103.0, 3.0]\n    ]\n  ]\n}\n```\n\n### Polygon\n\nGeoJSON polygons represent closed shapes on a map, like triangles, squares, dodecagons, or any shape with a fixed number of sides.\n\nTo specify a constraint specific to `Polygon`, it is useful to introduce the concept of a linear ring:\n\n- A linear ring is a closed `LineString` with four or more positions.\n- The first and last positions are equivalent, and they __must__ contain identical values; their representation __should__ also be identical.\n- A linear ring is the boundary of a surface or the boundary of a hole in a surface.\n- 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.\n\nThe `Polygon` geometry type definition as follows:\n\n- For type `Polygon`, the `coordinates` member __must__ be an array of linear ring coordinate arrays.\n- 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.\n\n```crystal\npolygon = GeoJSON::Polygon.new([\n  [[-10.0, -10.0], [10.0, -10.0], [10.0, 10.0], [-10.0,-10.0]],\n  [[-1.0, -2.0], [3.0, -2.0], [3.0, 2.0], [-1.0,-2.0]]\n])\npolygon.to_json\n```\n\n```json\n{\n  \"type\": \"Polygon\",\n  \"coordinates\": [\n    [\n      [-10.0, -10.0],\n      [10.0, -10.0],\n      [10.0,10.0],\n      [-10.0,-10.0]\n    ],\n    [\n      [-1.0, -2.0],\n      [3.0, -2.0],\n      [3.0, 2.0],\n      [-1.0,-2.0]\n    ]\n  ]\n}\n```\n\n### MultiPolygon\n\nFor type `MultiPolygon`, the `coordinates` member is an array of `Polygon` coordinate arrays.\n\n```crystal\npolygon1 = GeoJSON::Polygon.new(\n  [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]]\n)\npolygon2 = GeoJSON::Polygon.new(\n  [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]\n)\n\nmulti_polygon = GeoJSON::MultiPolygon.new([polygon1, polygon2])\nmulti_polygon.to_json\n```\n\n```json\n{\n  \"type\":\"MultiPolygon\",\n  \"coordinates\":[\n    [\n      [\n        [102.0,2.0],\n        [103.0,2.0],\n        [103.0,3.0],\n        [102.0,3.0],\n        [102.0,2.0]\n      ]\n    ],\n    [\n      [\n        [100.0,0.0],\n        [101.0,0.0],\n        [101.0,1.0],\n        [100.0,1.0],\n        [100.0,0.0]\n      ]\n    ]\n  ]\n}\n```\n\n### GeometryCollection\n\nA GeoJSON object with type `GeometryCollection` is a Geometry object.\n\nA `GeometryCollection` has a member with the name `geometries`. The\nvalue of `geometries` is an array. Each element of this array is a\nGeoJSON Geometry object.\n\n```crystal\npoint = GeoJSON::Point.new([100.0, 0.0])\nline_string = GeoJSON::LineString.new([\n  [101.0, 0.0],\n  [102.0, 1.0],\n])\npolygon = GeoJSON::Polygon.new([\n  [\n    [100.0, 0.0],\n    [101.0, 0.0],\n    [101.0, 1.0],\n    [100.0, 1.0],\n    [100.0, 0.0],\n  ],\n])\n\ngeometry_collection = GeoJSON::GeometryCollection.new([point, line_string, polygon])\ngeometry_collection.to_json\n```\n\n```json\n{\n  \"type\":\"GeometryCollection\",\n  \"geometries\":[\n    {\n      \"type\":\"Point\",\n      \"coordinates\":[100.0,0.0]\n    },\n    {\n      \"type\":\"LineString\",\n      \"coordinates\":[\n        [101.0,0.0],\n        [102.0,1.0]\n      ]\n    },\n    {\n      \"type\":\"Polygon\",\n      \"coordinates\":[\n        [\n          [100.0,0.0],\n          [101.0,0.0],\n          [101.0,1.0],\n          [100.0,1.0],\n          [100.0,0.0]\n        ]\n      ]\n    }\n  ]\n}\n```\n\n### Feature\n\nA `Feature` object represents a spatially bounded thing. Every `Feature` object is a GeoJSON object no matter where it occurs in a GeoJSON text.\n\n- A `Feature` object has a `\"type\"` member with the value `\"Feature\"`.\n- 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 the `Feature` is unlocated, a JSON `null` value.\n- A `Feature` object has a member with the name `\"properties\"`. The value of the properties member is an object (any JSON object or a JSON `null` value).\n- If a `Feature` has a commonly used identifier, that identifier __should__ be included as a member of the `Feature` object with the name `\"id\"`, and the value of this member is either a JSON string or number.\n\n```crystal\npoint = GeoJSON::Point.new([-80.1347334, 25.7663562])\nproperties = {\"color\" =\u003e \"red\"} of String =\u003e JSON::Any::Type\nfeature = GeoJSON::Feature.new(point, properties, id: 1)\nfeature.to_json\n```\n\n```json\n{\n  \"type\":\"Feature\",\n  \"geometry\":{\n    \"type\":\"Point\",\n    \"coordinates\":[-80.1347334,25.7663562]\n  },\n  \"properties\":{\n    \"color\":\"red\"\n  },\n  \"id\":1\n}\n```\n\n### FeatureCollection\n\nA 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.\n\n```crystal\nfeature1 = GeoJSON::Feature.new(\n  GeoJSON::Point.new([102.0, 0.5]),\n  id: \"point\"\n)\n\nfeature2 = GeoJSON::Feature.new(\n  GeoJSON::Polygon.new([\n    [\n      [100.0, 0.0],\n      [101.0, 0.0],\n      [101.0, 1.0],\n      [100.0, 1.0],\n      [100.0, 0.0],\n    ],\n  ]),\n  type: \"polygon\"\n)\n\nfeature_collection = GeoJSON::FeatureCollection.new([feature1, feature2])\nfeature_collection.to_json\n```\n\n```json\n{\n  \"type\":\"FeatureCollection\",\n  \"features\":[\n    {\n      \"type\":\"Feature\",\n      \"geometry\":{\n        \"type\":\"Point\",\n        \"coordinates\":[102.0,0.5]\n      },\n      \"properties\":null,\n      \"id\":\"point\"\n    },\n    {\n      \"type\":\"Feature\",\n      \"geometry\":{\n        \"type\":\"Polygon\",\n        \"coordinates\":[\n          [\n            [100.0,0.0],\n            [101.0,0.0],\n            [101.0,1.0],\n            [100.0,1.0],\n            [100.0,0.0]\n          ]\n        ]\n      },\n      \"properties\":null,\n      \"id\":\"polygon\"\n    }\n  ]\n}\n```\n\n## Foreign Members\n\nFor example, in the `Point` object shown below\n\n```json\n{\n  \"type\": \"Point\",\n  \"coordinates\": [-80.1347334, 25.7663562],\n  \"title\": \"Example Point\"\n}\n```\n\nthe name/value pair of `\"title\": \"Example Point\"` is a foreign member.\n\nGeoJSON semantics do not apply to foreign members and their descendants, regardless of their names and values.\n\nIf the GeoJSON type include foreign members, this properties in the JSON document will be stored in a `Hash(String, JSON::Any)`.\nOn serialization, any keys inside `json_unmapped` will be serialized and appended to the current json object.\n\n```crystal\npoint = GeoJSON::Point.new([-80.1347334, 25.7663562])\n\njson_unmapped = Hash(String, JSON::Any).new\njson_unmapped[\"title\"] = JSON::Any.new(\"Example Point\")\n\npoint.json_unmapped = json_unmapped\n```\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/geocrystal/geojson/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [Anton Maminov](https://github.com/mamantoha) - creator and maintainer\n\n## License\n\nCopyright: 2020-2024 Anton Maminov (anton.maminov@gmail.com)\n\nThis library is distributed under the MIT license. Please see the LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeocrystal%2Fgeojson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeocrystal%2Fgeojson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeocrystal%2Fgeojson/lists"}