Install geo_postgis
masterTo use geo_postgis in your Elixir project, add it to your mix.exs dependencies. Ensure that the PostGIS extension is also installed on your PostgreSQL database.
def deps do
[
{:geo_postgis, "~> 3.7"}
]
endrepository·master·Indexed 43 days ago
https://github.com/felt/geo_postgisA Postgrex extension for Elixir applications that enables support for PostGIS Geometry and Geography data types, leveraging the geo library for data structures. It provides integration for both Postgrex and Ecto, including macros for OpenGIS functions such as st_distance, st_dwithin, and various spatial relationship tests.
To use geo_postgis in your Elixir project, add it to your mix.exs dependencies. Ensure that the PostGIS extension is also installed on your PostgreSQL database.
def deps do
[
{:geo_postgis, "~> 3.7"}
]
endBefore using PostGIS features, you must ensure the extension is enabled in your database. You can do this via an Ecto migration.
defmodule MyApp.Repo.Migrations.EnablePostgis do
use Ecto.Migration
def change do
execute "CREATE EXTENSION IF NOT EXISTS postgis", "DROP EXTENSION IF EXISTS postgis"
end
endYou can create geometry or geography columns in migrations using the :geometry type. For more advanced PostGIS configurations (like specific SRIDs or geometry types), use execute/2 to run raw SQL commands like AddGeometryColumn.
# Simple geometry column
defmodule Repo.Migrations.Init do
use Ecto.Migration
def change do
create table(:test) do
add :name, :string
add :geom, :geometry
end
end
end
# Advanced configuration using raw SQL
defmodule Repo.Migrations.AdvancedInit do
use Ecto.Migration
def change do
create table(:test) do
add :name, :string
end
# Add a field with specific type and SRID (4326)
execute("SELECT AddGeometryColumn ('test','lng_lat_point',4326,'POINT',2);", "")
# Create a GIST index for spatial searches
execute("CREATE INDEX test_geom_idx ON test USING GIST (lng_lat_point);", "")
end
endWhen integrating with Ecto, you must define your custom types module by combining Geo.PostGIS.Extension with the standard Ecto.Adapters.Postgres.extensions(). You should also specify the json option in the definition if you want to control the JSON parser used for casting.
# 1. Define types module combining PostGIS and Postgres extensions
Postgrex.Types.define(MyApp.PostgresTypes,
[Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(),
json: Jason)
# 2. Configure your Repo to use these types
config :thanks, Repo,
database: "geo_postgrex_test",
username: "postgres",
password: "postgres",
hostname: "localhost",
adapter: Ecto.Adapters.Postgres,
types: MyApp.PostgresTypes
# 3. Use Geo.PostGIS.Geometry in your schemas
defmodule Test do
use Ecto.Schema
schema "test" do
field :name, :string
field :geom, Geo.PostGIS.Geometry
end
endWhen a binary is passed to Geo.PostGIS.Geometry.cast/1 (via Ecto.Type.cast/1), it is treated as a GeoJSON string. By default, geo_postgis uses the standard JSON library to convert this binary to a map. You can specify a different JSON module (e.g., Jason) in your config.exs using the json_library key.
# config.exs
config :geo_postgis,
json_library: JasonWhen Geo.PostGIS.Geometry.cast/1 receives a binary, it assumes the binary is a GeoJSON string. By default, it uses Geo.JSON to decode the string into a map.
If you need to use a different JSON parser for decoding these binaries, you can configure it via Geo.PostGIS.Config.json_library/0. This is useful if your application requires a specific JSON implementation for performance or compatibility reasons.
The Geo.PostGIS module provides a collection of macros that wrap PostGIS functions, allowing you to use spatial operations directly within Ecto queries. To use them, import the module into your query context.
Currently, only OpenGIS functions are implemented.
defmodule Example do
import Ecto.Query
import Geo.PostGIS
def example_query(geom) do
from location in Location,
limit: 5,
select: st_distance(location.geom, ^geom)
end
endTo use PostGIS geometry and geography types in your application, you must register Geo.PostGIS.Extension as a type module using Postgrex.Types.define/2.
If you are using Ecto with the Postgres adapter, you should include the Ecto Postgres extensions alongside the GeoPostGIS extension to ensure all standard Postgres types are also handled correctly.
# Standard Postgrex usage
Postgrex.Types.define(MyApp.PostgresTypes, [Geo.PostGIS.Extension], [])
# Recommended usage with Ecto
Postgrex.Types.define(MyApp.PostgresTypes,
[Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(),
json: Poison)To use PostGIS types directly with Postgrex, define a custom types module using Postgrex.Types.define/3 and include Geo.PostGIS.Extension. Pass this module to the types option when starting your connection.
# Define the types module
Postgrex.Types.define(MyApp.PostgresTypes, [Geo.PostGIS.Extension], [])
# Start the connection with the custom types
opts = [hostname: "localhost", username: "postgres", database: "geo_postgrex_test", types: MyApp.PostgresTypes]
{:ok, pid} = Postgrex.Connection.start_link(opts)
# Example usage
geo = %Geo.Point{coordinates: {30, -90}, srid: 4326}
{:ok, _} = Postgrex.Connection.query(pid, "INSERT INTO point_test VALUES ($1, $2)", [42, geo])You can use implemented OpenGIS functions in Ecto queries by importing Geo.PostGIS. This allows you to perform spatial operations like st_distance directly within your from macros.
defmodule Example do
import Ecto.Query
import Geo.PostGIS
def example_query(geom) do
query = from location in Location,
limit: 5,
select: st_distance(location.geom, ^geom)
query
|> Repo.one()
end
endBy default, geo_postgis uses the JSON module if it is loaded; otherwise, it falls back to Poison. This is used when a binary is passed to Geo.PostGIS.Geometry.cast/1 (via Ecto.Type.cast/1), where the binary is assumed to be a GeoJSON string. You can override the default JSON parser by setting the :json_library key in your application configuration under the :geo_postgis application name.
# In your config/config.exs or similar
config :geo_postgis, json_library: MyCustomJsonModuleOnce your custom types module is defined, pass it to the :types option when starting a Postgrex.Connection. This allows you to pass Geo structs (like %Geo.Point{}) directly into queries, and the extension will handle the encoding to WKB (Well-Known Binary) and decoding from the database.
# 1. Define types
Postgrex.Types.define(MyApp.PostgresTypes, [Geo.PostGIS.Extension], [])
# 2. Start connection with the custom types module
opts = [hostname: "localhost", username: "postgres", database: "geo_test", types: MyApp.PostgresTypes]
{:ok, pid} = Postgrex.Connection.start_link(opts)
# 3. Use Geo structs in queries
geo = %Geo.Point{coordinates: {30, -90}, srid: 4326}
{:ok, _} = Postgrex.Connection.query(pid, "INSERT INTO point_test VALUES ($1, $2)", [42, geo])
# 4. Retrieve Geo structs from results
{:ok, %Postgrex.Result{rows: [{id, geom}]}} = Postgrex.Connection.query(pid, "SELECT id, geom FROM point_test")
# geom will be a %Geo.Point{} struct