/ash_geo

Tools for using Geo, Topo and PostGIS with Ash

Primary LanguageElixirMIT LicenseMIT

AshGeo

All your Ash resources, in space!

Hex Hex Docs Downloads Build Status Coverage Status License GitHub Stars

AshGeo contains tools for using geospatial data in Ash resources and expressions, backed by PostGIS, Geo, Geo.PostGIS and [Topo].

It provides:

  • All the st_* functions that you would get with Geo.PostGIS for use with Ash expr, and more to come.
  • An Ash.Type backed by each of Geo.JSON, Geo.WKB and Geo.WKT which may be used as argument types in your Ash actions, and will automatically cast input from GeoJSON, WKT and WKB encodings.
  • An Ash.Type for Geo.PostGIS.Geometry, for use with resource attributes.
  • All types may be overridden and narrowed with use, allowing you to add stricter constraints and storage types (e.g. geometry(Point,26918)).
  • Validations for Geo types (such as is_point_zm(:arg) for checking that argument :arg is a instance of Geo.PointZM)
  • Validations backed by Topo, allowing checks of simple constraints such as contains? without needing to hit the database.

Installation

def deps do
  [
    {:ash_geo, "~> 0.2.0"},
  ]
end

This package provides a collection of non-overlapping functionality based on several dependencies, not all of which may be necessary your application. Therefore, the dependencies for the functionality you wish to use must be added alongside :ash_geo.

  • For Topo validations, :topo must be added.
  • For Postgis expressions, :geo_postgis must be added.

Configuration

config/config.exs:

# Geo.PostGIS: Use Jason coder
config :geo_postgis, json_library: Jason

# Ash: Type shorthands
config :ash, :custom_types, [
  geometry: AshGeo.Geometry,
  geo_json: AshGeo.GeoJson,
  geo_wkt: AshGeo.GeoWkt,
  geo_wkb: AshGeo.GeoWkb,
  geo_any: AshGeo.GeoAny,
  # You may add shorthands for any narrowed types here
  #point26918: CoolApp.Type.GeometryPoint26918,
]

config/runtime.exs:

# Postgrex: Geo.PostGIS types
Postgrex.Types.define(CoolApp.PostgresTypes,
  [Geo.PostGIS.Extension | Ecto.Adapters.Postgres.extensions()],
  json: Jason)

# Ecto: Geo.PostGIS types
config :cool_app, CoolApp.Repo, types: CoolApp.PostgresTypes

Usage

defmodule Area do
  use Ash.Resource, data_layer: AshPostgres.DataLayer

  import AshGeo.Postgis

  attributes do
    uuid_primary_key :id,
    attribute :geom, :geometry, allow_nil?: false
  end

  actions do
    create :create do
      argument :geom, :geo_any

      change set_attribute(:geom, arg(:geom))
    end

    read :containing do
      argument :geom, :geo_any do
        allow_nil? false
        constraints geo_types: :point
      end

      filter expr(^st_within(^arg(:geom), geom))
    end
  end

  code_interface do
    define_for Area
    define :create, args: [:geom]
    define :containing, args: [:geom]
  end
end

Try it out:

Area.create! "POLYGON ((30 0, 20 30, 0 10, 30 0))"
Area.create! "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"
Area.containing! "POINT(30 30)"
Area.containing! "POINT(20 20)"
Area.containing! "POINT(10 40)"
Area.containing! "POLYGON((0 0, 30 20, 40 30, 0 0))"

The full documentation can be found on HexDocs.

Roadmap

  • Add more PostGIS function wrappers (check out the PostGIS reference to see all that are available).
  • Continue to improve the test suite.
  • Replace validation macros with Spark DSL patches or similar.
  • Replace PostGIS fragment macros with custom predicates (ash#374)
  • Add datalayer-independent expression predicates backed by Topo.
  • Add more informative error messages (ash#365).

Developing

To get set up with the development environment, you will need a Postgres instance with support for the PostGIS extensions listed in test/support/repo.ex (the postgis/postgis image works nicely) and a superuser account ash_geo_test credentialed according to config/config.exs.

You may now generate and apply the test migrations:

mix ash_postgres.generate_migrations

AshGeo uses ex_check to bundle the test configuration, and simply running mix check should closely follow the configuration used in CI.

Contributing

If you have ideas or come across any bugs, feel free to open a pull request or an issue. You can also find me on the Ash Discord as @\.

License

MIT License

Copyright (c) 2023 bcksl

See LICENSE.md for details.