{"id":19944986,"url":"https://github.com/geocrystal/geo","last_synced_at":"2025-10-03T18:41:54.984Z","repository":{"id":66224763,"uuid":"220478659","full_name":"geocrystal/geo","owner":"geocrystal","description":"Geospatial primitives and algorithms for Crystal","archived":false,"fork":false,"pushed_at":"2025-01-06T09:06:32.000Z","size":102,"stargazers_count":20,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-07T18:25:40.212Z","etag":null,"topics":["algorithms","convex-hull","crystal","geo","geojson","geolocation","geospatial-primitives","hacktoberfest","haversine","polygons","ring-area"],"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":"2019-11-08T14:02:49.000Z","updated_at":"2025-01-06T09:05:18.000Z","dependencies_parsed_at":"2024-04-30T08:58:07.839Z","dependency_job_id":null,"html_url":"https://github.com/geocrystal/geo","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geocrystal%2Fgeo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/geocrystal","download_url":"https://codeload.github.com/geocrystal/geo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252216092,"owners_count":21713098,"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":["algorithms","convex-hull","crystal","geo","geojson","geolocation","geospatial-primitives","hacktoberfest","haversine","polygons","ring-area"],"created_at":"2024-11-13T00:23:43.066Z","updated_at":"2025-10-03T18:41:49.920Z","avatar_url":"https://github.com/geocrystal.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Geo\n\n![Crystal CI](https://github.com/geocrystal/geo/workflows/Crystal%20CI/badge.svg)\n[![GitHub release](https://img.shields.io/github/release/geocrystal/geo.svg)](https://github.com/geocrystal/geo/releases)\n[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://geocrystal.github.io/geo/)\n[![License](https://img.shields.io/github/license/geocrystal/geo.svg)](https://github.com/geocrystal/geo/blob/master/LICENSE)\n\nGeospatial primitives, algorithms, and utilities for Crystal.\n\n## Installation\n\nAdd this to your application's `shard.yml`:\n\n```yaml\ndependencies:\n  geo:\n    github: geocrystal/geo\n```\n\nRun `shards install`\n\n## Usage\n\nA `Geo::Coord` is a point in geographical coordinates: latitude and longitude.\n\n```crystal\nrequire \"geo\"\n\nc = Geo::Coord.new(50.004444, 36.231389)\n\nc.strfcoord(%{%latd %latm' %0.1lats\" %lath, %lngd %lngm' %0.1lngs\" %lngh})\n# =\u003e \"50 0' 16.0\" N, 36 13' 53.0\" E\"\n\nc.strfcoord(\"%lat,%lng\")\n# =\u003e \"-50.004444,-36.231389\"\n\nc.to_s\n# =\u003e \"50°0'16\"N 36°13'53\"E\"\n\npos = Geo::Coord.new(50.004444, 36.231389)\n\npos.geohash\n# =\u003e \"ubcu2rnbuxcx\"\n\npos.geohash(5)\n# =\u003e \"ubcu2\"\n```\n\n### Polygon\n\nA polygon represents an area enclosed by a closed path (or loop), which is defined by a series of coordinates.\n\n```crystal\nrequire \"geo\"\n\npos1 = Geo::Coord.new(45.3142533036254, -93.47527313511819)\npos2 = Geo::Coord.new(45.31232182518015, -93.34893036168069)\npos3 = Geo::Coord.new(45.23694281999268, -93.35167694371194)\npos4 = Geo::Coord.new(45.23500870841669, -93.47801971714944)\n\npolygon = Geo::Polygon.new([pos1, pos2, pos3, pos4])\n```\n\nThe Polygon in the example above consists of four sets of `Geo::Coord` coordinates, but notice that the first and last sets define the same location, which completes the loop. In practice, however, since polygons define closed areas, you don't need to specify the last set of coordinates. Ot will automatically complete the polygon by connecting the last location back to the first location.\n\nThe following example is identical to the previous one, except that the last `Geo::Coord` is omitted:\n\n```crystal\nrequire \"geo\"\n\npos1 = Geo::Coord.new(45.3142533036254, -93.47527313511819)\npos2 = Geo::Coord.new(45.31232182518015, -93.34893036168069)\npos3 = Geo::Coord.new(45.23694281999268, -93.35167694371194)\n\npolygon = Geo::Polygon.new([pos1, pos2, pos3])\n```\n\nAdditional actions:\n\n```crystal\ncoord_inside = Geo::Coord.new(45.27428243796789, -93.41648483416066)\ncoord_outside = Geo::Coord.new(45.45411010558687, -93.78151703160256)\n\npolygon.contains?(coord_inside)  # =\u003e true\npolygon.contains?(coord_outside) # =\u003e false\n\npolygon.centroid # =\u003e Geo::Coord(@lat=45.27463866133501, @lng=-93.41400121829719)\n```\n\nAdditionally you can initialize polygon as [convex hull](https://en.wikipedia.org/wiki/Convex_hull) from coordinates of points.\n\n```crystal\npoints = [\n  {1.0, 1.0},\n  {1.0, 0.0},\n  {1.0, -1.0},\n  {0.0, -1.0},\n  {-1.0, -1.0},\n  {-1.0, 0.0},\n  {-1.0, 1.0},\n  {0.0, 1.0},\n  {0.0, 0.0},\n].map { |point| Geo::Coord.new(point[0], point[1]) }\n\npolygon = Geo::Polygon.new(points, convex_hull: true)\npolygon.coords\n# =\u003e {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0}, {-1.0, -1.0}\n```\n\nThe convex hull is computed using the [convex_hull](https://github.com/geocrystal/convex_hull) library.\n\n### Formatting\n\n`Geo::Coord#strfcoord` formats coordinates according to directives.\n\nEach directive starts with `%` and can contain some modifiers before its name.\n\nAcceptable modifiers:\n\n- unsigned integers: none;\n- signed integers: `+` for mandatory sign printing;\n- floats: same as integers and number of digits modifier, like `0.3`.\n\nList of directives:\n\n| Directive | Description                                 |\n| --------- | ------------------------------------------- |\n| `%lat`    | Full latitude, floating point, signed       |\n| `%latds`  | Latitude degrees, integer, signed           |\n| `%latd`   | Latitude degrees, integer, unsigned         |\n| `%latm`   | Latitude minutes, integer, unsigned         |\n| `%lats`   | Latitude seconds, floating point, unsigned  |\n| `%lath`   | Latitude hemisphere, \"N\" or \"S\"             |\n| `%lng`    | Full longitude, floating point, signed      |\n| `%lngds`  | Longitude degrees, integer, signed          |\n| `%lngd`   | Longitude degrees, integer, unsigned        |\n| `%lngm`   | Longitude minutes, integer, unsigned        |\n| `lngs`    | Longitude seconds, floating point, unsigned |\n| `%lngh`   | Longitude hemisphere, \"E\" or \"W\"            |\n\nExamples:\n\n```crystal\ng = Geo::Coord.new(50.004444, 36.231389)\ng.strfcoord('%+lat, %+lng')\n# =\u003e \"+50.004444, +36.231389\"\ng.strfcoord(\"%latd°%latm'%lath -- %lngd°%lngm'%lngh\")\n# =\u003e \"50°0'N -- 36°13'E\"\n```\n\n`strfcoord` handles seconds rounding implicitly:\n\n```crystal\npos = Geo::Coord.new(0.033333, 91.333333)\npos.strfcoord('%latd %latm %0.5lats') # =\u003e \"0 1 59.99880\"\npos.strfcoord('%latd %latm %lats')  # =\u003e \"0 2 0\"\n```\n\n### Calculate distances between two coords\n\nHaversine formula from [haversine](https://github.com/geocrystal/haversine) shard is used.\n\n```crystal\nrequire \"geo\"\nrequire \"geo/distance\"\n\nlondon = Geo::Coord.new(51.500153, -0.126236)\nnew_york = Geo::Coord.new(40.714268, -74.005974)\n\nnew_york.distance(london).to_kilometers\n# =\u003e 5570.4744596620685\n```\n\n### Calculates the location of a destination coord\n\n```crystal\nrequire \"geo\"\nrequire \"geo/distance\"\n\npoint = Geo::Coord.new(39, -75)\n\npoint.destination(5000, 90, :kilometers)\n# Geo::Coord(@lat=26.440010707631124, @lng=-22.885355549364313)\n```\n\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/geocrystal/geo/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","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeocrystal%2Fgeo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeocrystal%2Fgeo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeocrystal%2Fgeo/lists"}