AshGeo
All your Ash resources, in space!
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 withGeo.PostGISfor use with Ashexpr, and more to come. - An
Ash.Typebacked by each ofGeo.JSON,Geo.WKBandGeo.WKTwhich may be used asargumenttypes in your Ash actions, and will automatically cast input from GeoJSON, WKT and WKB encodings. - An
Ash.TypeforGeo.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
Geotypes (such asis_point_zm(:arg)for checking that argument:argis a instance ofGeo.PointZM) - Validations backed by
Topo, allowing checks of simple constraints such ascontains?without needing to hit the database.
Installation
def deps do
[
{:ash_geo, "~> 0.3.0"},
]
endThis 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
Topovalidations,:topomust be added. - For Postgis expressions,
:geo_postgismust 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.PostgresTypesUsage
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
endTry 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
fragmentmacros 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.