Geo

repository·master·Indexed 20 days ago

https://github.com/felt/geo

A collection of GIS functions for Elixir that handles conversions to and from WKT, WKB, and GeoJSON for various geometry types, including Point, LineString, Polygon, and their Multi/Z/M variants.

Tokens
4.5K
Snippets
22
Records
23
Agent score
68%

What's inside geo

  1. Install the Geo library

    master

    To use Geo in your Elixir project, add it to your mix.exs dependencies list. Use the ~> 4.0 version constraint to ensure compatibility with the v4 series.

    defp deps do
      [
        {:geo, "~> 4.0"}
      ]
    end
  2. Support SRID in WKT strings

    master

    The Geo.WKT.Decoder supports WKT strings that include a Spatial Reference System Identifier (SRID). To include an SRID, format the string by prefixing the WKT with SRID= followed by the integer ID and a semicolon. The decoder will extract the integer and assign it to the srid field of the resulting Geo.geometry struct.

    # Decoding a WKT string with an SRID
    # Format: "SRID=ID;WKT"
    {:ok, geometry} = Geo.WKT.Decoder.decode("SRID=4326;POINT(1 1)")
    
    # The resulting struct will have the srid field set
    # %Geo.Point{coordinates: {1.0, 1.0}, srid: 4326}
  3. JSON encoding for Geo geometries

    master

    The Geo library provides automatic JSON encoding for all geometry types. It detects and uses the available JSON library (JSON or Jason) to encode geometries via Geo.JSON.encode!/1.

    # If Jason is loaded, Geo.Point implements Jason.Encoder
    geo = %Geo.Point{x: 1.0, y: 2.0}
    Jason.encode!(geo)
  4. Convert GeometryCollections to GeoJSON FeatureCollections

    master

    When calling Geo.JSON.Encoder.encode!/2 with a %Geo.GeometryCollection{} and the option feature: true, the encoder translates the collection into a GeoJSON FeatureCollection.

    In this mode, the properties defined on the GeometryCollection are merged into each individual Feature within the collection. Properties defined on the encapsulated Geo.geometry() structs will override the properties inherited from the collection.

    # If geom is a %Geo.GeometryCollection{properties: %{a: 1}, geometries: [...]} 
    # and you call:
    Geo.JSON.Encoder.encode!(geom, feature: true)
    
    # The result is a %{"type" => "FeatureCollection", "features" => [...]} 
    # where each feature's properties include %{"a" => 1}
  5. String representation of geometries via WKT

    master

    When the :impl_to_string compilation environment variable is set to true, all Geo geometry types implement the String.Chars protocol. This allows you to convert any geometry to its Well-Known Text (WKT) representation using to_string/1 or string interpolation.

    # Requires compilation with :impl_to_string set to true
    geo = %Geo.Point{x: 1.0, y: 2.0}
    "#{geo}" # Uses Geo.WKT.encode!/1 internally
  6. Encode and decode WKB and EWKB

    master

    Use Geo.WKB to handle Well-Known Binary (WKB) and Extended WKB (EWKB) formats. This is useful for low-level binary geospatial data exchange.

    • Geo.WKB.decode(binary): Returns {:ok, geometry} or {:error, reason}.
    • Geo.WKB.decode!(binary): Returns the geometry struct or raises an error.
    • Geo.WKB.encode!(geometry): Returns the WKB binary string.
    {:ok, point} = Geo.WKB.decode("0101000000000000000000F03F000000000000F03F")
    %Geo.Point{ coordinates: {1.0, 1.0}, srid: nil }
    
    Geo.WKB.encode!(point)
    "00000000013FF00000000000003FF0000000000000"
    
    point = Geo.WKB.decode!("0101000020E61000009EFB613A637B4240CF2C0950D3735EC0")
    %Geo.Point{ coordinates: {36.9639657, -121.8097725}, srid: 4326 }
    
    Geo.WKB.encode!(point)
    "0020000001000010E640427B633A61FB9EC05E73D350092CCF"
  7. Encode and decode GeoJSON

    master

    Geo.JSON handles the conversion between Geo structs and maps that follow the GeoJSON specification.

    Important: Geo.JSON does not perform JSON parsing/stringification itself. You must use a JSON library (like Jason) to convert between JSON strings and maps before/after using Geo.JSON.

    • Geo.JSON.encode(geometry): Returns {:ok, map} where the map is shaped as GeoJSON.
    • Geo.JSON.decode(map): Returns {:ok, geometry} from a GeoJSON-shaped map.
    • Geo.JSON.encode!(geometry): Returns a GeoJSON-shaped map.
    # Convert struct to GeoJSON-shaped map
    Geo.JSON.encode(point)
    {:ok, %{ "type" => "Point", "coordinates" => [100.0, 0.0] }}
    
    # Convert GeoJSON-shaped map to struct (requires external JSON parser)
    point = JSON.decode!("{\"type\": \"Point\", \"coordinates\": [100.0, 0.0] }") |> Geo.JSON.decode
    {:ok, %Geo.Point{coordinates: {100.0, 0.0}, srid: 4326, properties: %{}}
    
    # Convert struct to JSON string (requires external JSON parser)
    Geo.JSON.encode!(point) |> JSON.encode!
    "{\"coordinates\":[100.0,0.0],\"type\":\"Point\"}"
  8. Encode and decode WKT and EWKT

    master

    Use Geo.WKT to convert between Well-Known Text (WKT) strings and Geo structs. It supports both standard WKT and Extended WKT (EWKT) which includes Spatial Reference System Identifiers (SRID).

    • Geo.WKT.decode(wkt_string): Returns {:ok, geometry} or {:error, reason}.
    • Geo.WKT.decode!(wkt_string): Returns the geometry struct or raises an error.
    • Geo.WKT.encode!(geometry): Returns the WKT string representation.
    {:ok, point} = Geo.WKT.decode("POINT(30 -90)")
    %Geo.Point{ coordinates: {30, -90}, srid: nil}
    
    Geo.WKT.encode!(point)
    "POINT(30 -90)"
    
    point = Geo.WKT.decode!("SRID=4326;POINT(30 -90)")
    %Geo.Point{coordinates: {30, -90}, srid: 4326}
  9. WKB Encoding and Decoding Examples

    master

    The following examples demonstrate common usage patterns for Geo.WKB:

    # Decode a base-16 string to a Point
    {:ok, point} = Geo.WKB.decode("0101000000000000000000F03F000000000000F03F")
    # => Geo.Point[coordinates: {1, 1}, srid: nil]
    
    # Decode a base-16 string with an SRID
    point = Geo.WKB.decode!("01010002E61000009EFB613A637B4240CF2C0950D3735EC0")
    # => Geo.Point[coordinates: {36.9639657, -121.8097725}, srid: 4326]
    {:ok, point} = Geo.WKB.decode("0101000000000000000000F03F000000000000F03F")
          Geo.Point[coordinates: {1, 1}, srid: nil]
    
    iex> Geo.WKT.encode!(point)
          "POINT(1 1)"
    
    iex> point = Geo.WKB.decode!("01010002E61000009EFB613A637B4240CF2C0950D3735EC0")
          Geo.Point[coordinates: {36.9639657, -121.8097725}, srid: 4326]
  10. Decode GeoJSON maps using Geo.JSON.Decoder

    master

    The Geo.JSON.Decoder module provides functions to convert GeoJSON maps (typically parsed from JSON) into Geo geometry structs.

    It supports several GeoJSON object types:

    • Geometry objects: Direct geometry types like Point, LineString, Polygon, etc.
    • Feature objects: Individual features containing geometry and properties.
    • FeatureCollections: Collections of features.
    • GeometryCollections: Collections of geometries.

    Key Behaviors:

    • SRID Handling: If no CRS (Coordinate Reference System) is provided in the GeoJSON, the decoder defaults to SRID 4326 (WGS 84) per the GeoJSON spec.
    • Null Geometries: Feature objects with null geometries are stripped from a FeatureCollection. A standalone Feature with null geometry returns nil.
    • Coordinate Conversion: Coordinates are converted from JSON lists to Elixir tuples where appropriate (e.g., {x, y}).
    # To decode safely with error handling:
    case Geo.JSON.Decoder.decode(geo_json_map) do
      {:ok, %Geo.Point{}} -> # handle success
      {:error, %Geo.JSON.Decoder.DecodeError{}} -> # handle error
    end
    
    # To decode and raise on error:
    geometry = Geo.JSON.Decoder.decode!(geo_json_map)
  11. Encode Geo geometry to GeoJSON maps with Geo.JSON.Encoder.encode/2

    master

    Use Geo.JSON.Encoder.encode/2 to safely convert a Geo.geometry() struct into a map representing its GeoJSON equivalent. This function returns {:ok, map} on success or {:error, %Geo.JSON.Encoder.EncodeError{}} if encoding fails.

    Note that the encoder disregards SRID information for the geometry itself (as GeoJSON is expected to be in WGS 84), but it will include a crs field in the resulting map if an SRID is present.

    # Returns {:ok, map} or {:error, EncodeError}
    Geo.JSON.Encoder.encode(geom)
  12. Encode geometries to raw iodata

    master

    If you need the raw byte sequence instead of a base-16 string, use Geo.WKB.encode_to_iodata/2. This returns the WKB as iodata (a sequence of bytes).

    Geo.WKB.encode_to_iodata(geom, :xdr)