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.PostGIS
for use with Ashexpr
, and more to come. - An
Ash.Type
backed by each ofGeo.JSON
,Geo.WKB
andGeo.WKT
which may be used asargument
types in your Ash actions, and will automatically cast input from GeoJSON, WKT and WKB encodings. - An
Ash.Type
forGeo.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 asis_point_zm(:arg)
for checking that argument:arg
is a instance ofGeo.PointZM
) - Validations backed by
Topo
, allowing checks of simple constraints such ascontains?
without needing to hit the database.
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.
# 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,
]
# 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
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.
- 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
).
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.
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 @\
.
MIT License
Copyright (c) 2023 bcksl
See LICENSE.md for details.