### Start Bare Postgrex Connection with Custom Types Source: https://context7.com/felt/geo_postgis/llms.txt Start a bare Postgrex connection, specifying the custom types module that includes the Geo.PostGIS.Extension. ```elixir # Start a bare Postgrex connection with the custom types {:ok, pid} = Postgrex.start_link( hostname: "localhost", database: "my_app_dev", username: "postgres", types: MyApp.PostgresTypes ) ``` -------------------------------- ### Get Start and End Points with ST_StartPoint and ST_EndPoint Source: https://context7.com/felt/geo_postgis/llms.txt Extract the first and last vertex of a linestring or the start and end points of the exterior ring of a polygon. ```elixir # Start and end points from(road in Road, select: %{start: st_start_point(road.geom), finish: st_end_point(road.geom)} ) ``` -------------------------------- ### Get Exterior Ring with ST_ExteriorRing Source: https://context7.com/felt/geo_postgis/llms.txt Extract the exterior boundary ring of a polygon geometry. ```elixir # Exterior ring of a polygon from(r in Region, select: st_exterior_ring(r.geom)) ``` -------------------------------- ### Calculate Geometry Length Source: https://context7.com/felt/geo_postgis/llms.txt Calculates the length of a linear geometry. This example orders results by length in descending order. ```elixir import Geo.PostGIS import Ecto.Query # Length of a line from(road in Road, order_by: [desc: st_length(road.geom)], limit: 5, select: road) ``` -------------------------------- ### Configure GeoJSON JSON Library Source: https://context7.com/felt/geo_postgis/llms.txt Configures the JSON library used for GeoJSON casting in Geo.PostGIS.Geometry. The library must implement a `decode/1` function. Examples show configuration for Jason, Poison, and the built-in JSON module. ```elixir # config/config.exs — choose Jason (recommended) config :geo_postgis, json_library: Jason # config/config.exs — use Poison config :geo_postgis, json_library: Poison # config/config.exs — use Elixir 1.18+ built-in JSON module config :geo_postgis, json_library: JSON # Query the configured library at runtime iex> Geo.PostGIS.Config.json_library() Jason # The default resolution order (compile-time): # 1. JSON (Elixir 1.18+) # 2. Poison (fallback) # Explicitly set via config to avoid ambiguity in projects with multiple JSON libs. ``` -------------------------------- ### Filter by Distance (ST_DWithin) Source: https://context7.com/felt/geo_postgis/llms.txt Filters geometries that are within a specified distance. The first example uses geometry units (degrees), while the second uses meters with an option for spheroid calculation. ```elixir import Geo.PostGIS import Ecto.Query # ST_DWithin — geometry units (filter rows within 0.01 degrees) from(l in Location, where: st_dwithin(l.geom, ^search_point, 0.01), select: l) # ST_DWithin — geography units (meters), with spheroid option search_point = %Geo.Point{coordinates: {-73.9857, 40.7484}, srid: 4326} from(l in Location, where: st_dwithin(l.geom, ^search_point, 500.0, true), # 500 m, use spheroid select: l.name) ``` -------------------------------- ### Get Bounding Rectangle with ST_Envelope Source: https://context7.com/felt/geo_postgis/llms.txt Retrieve the smallest axis-aligned bounding rectangle for a geometry. ```elixir # ST_Envelope — bounding rectangle from(r in Region, select: st_envelope(r.geom)) ``` -------------------------------- ### Extract Interior Rings and Points Source: https://context7.com/felt/geo_postgis/llms.txt Extracts the number of interior rings and a specific interior ring from a geometry. Also shows how to get all points from a road geometry as a multipoint. ```elixir from(r in Region, select: %{hole_count: st_num_interior_rings(r.geom), ring: st_interior_ring_n(r.geom, 1)}) # All points as a multipoint from(road in Road, select: st_points(road.geom)) ``` -------------------------------- ### Insert Geography Point Source: https://context7.com/felt/geo_postgis/llms.txt Example of inserting a Geo.Point into a PostGIS 'geography' column using Postgrex. ```elixir point = %Geo.Point{coordinates: {2.2945, 48.8584}, srid: 4326} Postgrex.query!(pid, "INSERT INTO places (geom) VALUES ($1::geography)", [point]) ``` -------------------------------- ### Get Geometry Memory Size Source: https://context7.com/felt/geo_postgis/llms.txt Retrieves the memory size of a geometry in bytes. Requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # Memory size in bytes from(l in Location, select: %{name: l.name, bytes: st_mem_size(l.geom)}) ``` -------------------------------- ### Get Geometry SRID Source: https://context7.com/felt/geo_postgis/llms.txt Retrieves the Spatial Reference System Identifier (SRID) of a geometry. ```elixir import Geo.PostGIS import Ecto.Query # SRID from(l in Location, select: st_srid(l.geom)) ``` -------------------------------- ### Extract Line Substring with ST_LineSubstring Source: https://context7.com/felt/geo_postgis/llms.txt Extract a portion of a line segment defined by start and end fractional distances along its length. ```elixir # Extract a substring of a line between two fractions from(road in Road, select: st_line_substring(road.geom, 0.2, 0.8)) ``` -------------------------------- ### Get Geometry Type Source: https://context7.com/felt/geo_postgis/llms.txt Returns the geometry type as a string (e.g., 'ST_Point', 'ST_Polygon'). Requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # Geometry type as a string (e.g., "ST_Point", "ST_Polygon") from(l in Location, select: st_geometry_type(l.geom)) ``` -------------------------------- ### Extract Nth Point with ST_PointN Source: https://context7.com/felt/geo_postgis/llms.txt Get a specific vertex (point) from a linestring or the boundary of a polygon using a 1-based index. ```elixir # Nth point on a linestring from(road in Road, select: st_point_n(road.geom, 1)) ``` -------------------------------- ### Repair Invalid Geometries Source: https://context7.com/felt/geo_postgis/llms.txt Updates invalid geometries to valid ones using `st_make_valid`. This example targets rows where `st_is_valid` returns false. ```elixir import Geo.PostGIS import Ecto.Query # Repair invalid geometries from(r in Region, update: [set: [geom: st_make_valid(r.geom)]], where: not st_is_valid(r.geom)) ``` -------------------------------- ### Enable PostGIS Extension in Ecto Migration Source: https://github.com/felt/geo_postgis/blob/master/README.md Execute `CREATE EXTENSION IF NOT EXISTS postgis` in your migration to enable PostGIS functionality. ```elixir 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 end ``` -------------------------------- ### Check Spatial Relationships (Others) Source: https://context7.com/felt/geo_postgis/llms.txt Demonstrates various other spatial relationship predicates like ST_Equals, ST_Disjoint, ST_Touches, and ST_Overlaps. Ensure Geo.PostGIS and Ecto.Query are imported. ```elixir import Geo.PostGIS import Ecto.Query # ST_Equals, ST_Disjoint, ST_Touches, ST_Overlaps from(l in Location, where: st_equals(l.geom, ^exact_geom), select: l.id) from(l in Location, where: st_disjoint(l.geom, ^region), select: l.name) from(l in Location, where: st_touches(l.geom, ^boundary), select: l) from(l in Location, where: st_overlaps(l.geom, ^other_area), select: l) ``` -------------------------------- ### Define Postgrex Types and Connect to Database Source: https://github.com/felt/geo_postgis/blob/master/README.md Define custom Postgrex types including Geo.PostGIS.Extension and establish a database connection. Assumes a running PostgreSQL instance. ```elixir Postgrex.Types.define(MyApp.PostgresTypes, [Geo.PostGIS.Extension], []) opts = [hostname: "localhost", username: "postgres", database: "geo_postgrex_test", types: MyApp.PostgresTypes ] [hostname: "localhost", username: "postgres", database: "geo_postgrex_test", types: MyApp.PostgresTypes] {:ok, pid} = Postgrex.Connection.start_link(opts) {:ok, #PID<0.115.0>} ``` -------------------------------- ### Insert GeoJSON Point into PostGIS Table Source: https://github.com/felt/geo_postgis/blob/master/README.md Demonstrates creating a Geo.Point struct, creating a table, and inserting the point into the 'point_test' table using Postgrex. ```elixir geo = %Geo.Point{coordinates: {30, -90}, srid: 4326} %Geo.Point{coordinates: {30, -90}, srid: 4326} {:ok, _} = Postgrex.Connection.query(pid, "CREATE TABLE point_test (id int, geom geometry(Point, 4326))") {:ok, %Postgrex.Result{columns: nil, command: :create_table, num_rows: 0, rows: nil}} {:ok, _} = Postgrex.Connection.query(pid, "INSERT INTO point_test VALUES ($1, $2)", [42, geo]) {:ok, %Postgrex.Result{columns: nil, command: :insert, num_rows: 1, rows: nil}} ``` -------------------------------- ### Construct Points with ST_Point and ST_MakePoint Source: https://context7.com/felt/geo_postgis/llms.txt Create new point geometries from longitude and latitude values. ST_MakePoint supports 2D, 3D, and 4D points. ```elixir import Geo.PostGIS import Ecto.Query # Create a point from column values from(l in Location, select: st_point(l.longitude, l.latitude)) ``` ```elixir # ST_MakePoint — 2D, 3D, or 4D from(l in Location, select: st_make_point(l.lon, l.lat)) from(l in Location, select: st_make_point(l.lon, l.lat, l.altitude)) from(l in Location, select: st_make_point(l.lon, l.lat, l.altitude, l.measure)) ``` -------------------------------- ### Create Bounding Box with ST_MakeBox2D Source: https://context7.com/felt/geo_postgis/llms.txt Construct a bounding box (rectangular polygon) using two corner points. ```elixir # Bounding envelope from two corner points from(r in Region, select: st_make_box_2d(st_point(0.0, 0.0), st_point(10.0, 10.0))) ``` -------------------------------- ### Add geo_postgis to Dependencies Source: https://context7.com/felt/geo_postgis/llms.txt Add the geo_postgis library and optional dependencies like jason, ecto_sql, and postgrex to your mix.exs file. ```elixir # mix.exs defp deps do [ {:geo_postgis, "~> 3.7"}, {:jason, "~> 1.2"}, # optional, recommended JSON library {:ecto_sql, "~> 3.0"}, # optional, for Ecto integration {:postgrex, ">= 0.0.0"} ] end ``` -------------------------------- ### Snap Geometry to Grid with ST_SnapToGrid Source: https://context7.com/felt/geo_postgis/llms.txt Adjust the coordinates of a geometry to align with a specified grid, reducing precision. ```elixir # Snap to grid for precision reduction from(r in Region, select: st_snap_to_grid(r.geom, 0.0001)) ``` -------------------------------- ### Serialize Geometries to WKT, WKB, and GeoJSON Source: https://context7.com/felt/geo_postgis/llms.txt Converts geometry values to Well-Known Text (WKT), Well-Known Binary (WKB), or GeoJSON representations within queries. Requires importing Geo.PostGIS and Ecto.Query. ```elixir import Geo.PostGIS import Ecto.Query # WKT (Well-Known Text) output from(l in Location, select: st_as_text(l.geom)) # => "POINT(2.2945 48.8584)" # WKB (Well-Known Binary) output from(l in Location, select: st_as_binary(l.geom)) # GeoJSON of the entire row — note: takes the table binding (l), not a field from(l in Location, select: st_as_geojson(l)) # => {"type":"Feature","geometry":{"type":"Point","coordinates":[2.2945,48.8584]}, # "properties":{"name":"Eiffel Tower",...}} # Decode GeoJSON result back to a Geo struct results = MyApp.Repo.all(from l in Location, select: st_as_geojson(l)) geom_structs = Enum.map(results, fn json_str -> json_str |> Jason.decode!() |> Geo.JSON.decode!() end) ``` -------------------------------- ### Interpolate Multiple Points on Line with ST_LineInterpolatePoints Source: https://context7.com/felt/geo_postgis/llms.txt Generate multiple points along a line at specified fractional intervals. Setting 'repeat' to true includes points at each interval. ```elixir # Find multiple interpolated points (repeat = true places one at each fraction interval) from(road in Road, select: st_line_interpolate_points(road.geom, 0.25, true)) ``` -------------------------------- ### Configure geo_postgis JSON Library and Repo Source: https://context7.com/felt/geo_postgis/llms.txt Configure the geo_postgis library to use a specific JSON library and set up your application's Ecto repository with the custom types module. ```elixir # config/config.exs config :geo_postgis, json_library: Jason config :my_app, MyApp.Repo, types: MyApp.PostgresTypes, database: "my_app_dev", hostname: "localhost" ``` -------------------------------- ### Create Envelope with ST_MakeEnvelope Source: https://context7.com/felt/geo_postgis/llms.txt Generate a rectangular polygon from minimum and maximum coordinate bounds, specifying the SRID. ```elixir # ST_MakeEnvelope — create a rectangular polygon from min/max bounds from(r in Region, where: st_intersects(r.geom, st_make_envelope(-74.1, 40.6, -73.7, 40.9, 4326)), select: r ) ``` -------------------------------- ### Check Geometry Properties (Empty, Collection, Dimension) Source: https://context7.com/felt/geo_postgis/llms.txt Checks if a geometry is empty, if it's a collection, and its dimension. Imports for Geo.PostGIS and Ecto.Query are needed. ```elixir import Geo.PostGIS import Ecto.Query # Is empty, is collection, dimension from(l in Location, where: not st_is_empty(l.geom), select: %{dim: st_dimension(l.geom), is_coll: st_is_collection(l.geom)}) ``` -------------------------------- ### Configure Optional JSON Library Source: https://github.com/felt/geo_postgis/blob/master/README.md Configure a custom JSON library for GeoJSON string parsing if needed. Defaults to the built-in JSON module. ```elixir config :geo_postgis, json_library: Jason # If you want to set your JSON module ``` -------------------------------- ### Configure Ecto Repo with GeoPostGIS Extensions Source: https://github.com/felt/geo_postgis/blob/master/README.md Configures the Ecto repository to use Geo.PostGIS.Extension and specifies the custom types module. ```elixir # If using with Ecto, you may want something like this instead Postgrex.Types.define(MyApp.PostgresTypes, [Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(), json: Jason) # Add extensions to your repo config config :thanks, Repo, database: "geo_postgrex_test", username: "postgres", password: "postgres", hostname: "localhost", adapter: Ecto.Adapters.Postgres, types: MyApp.PostgresTypes ``` -------------------------------- ### Simplify Geometry with ST_SimplifyPreserveTopology Source: https://context7.com/felt/geo_postgis/llms.txt Reduce the number of vertices in a geometry while ensuring that the topological properties are maintained. ```elixir # Simplify while preserving topology from(r in Region, select: st_simplify_preserve_topology(r.geom, 0.001)) ``` -------------------------------- ### Make Valid with Strategy Source: https://context7.com/felt/geo_postgis/llms.txt Uses `st_make_valid` with specific strategy parameters (available in PostGIS 3.1+). Requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # st_make_valid with strategy parameters (PostGIS 3.1+) from(r in Region, select: st_make_valid(r.geom, "method=structure")) ``` -------------------------------- ### Add GeoPostGIS to Mix Dependencies Source: https://github.com/felt/geo_postgis/blob/master/README.md Add the :geo_postgis package to your `mix.exs` file to include it in your project. ```elixir def deps do [ {:geo_postgis, "~> 3.7"} ] end ``` -------------------------------- ### Create Geometry from WKT with ST_GeomFromText Source: https://context7.com/felt/geo_postgis/llms.txt Construct a geometry object from its Well-Known Text (WKT) representation, optionally specifying the SRID. ```elixir # From WKT with optional SRID from(l in Location, select: st_geom_from_text("POINT(2.2945 48.8584)", 4326) ) ``` -------------------------------- ### Configure Postgrex Types Module Source: https://context7.com/felt/geo_postgis/llms.txt Define a Postgrex types module to include the Geo.PostGIS.Extension. This can be done bare or with Ecto integration, specifying the JSON library if needed. ```elixir # lib/my_app/postgres_types.ex — bare Postgrex Postgrex.Types.define(MyApp.PostgresTypes, [Geo.PostGIS.Extension], []) ``` ```elixir # lib/my_app/postgres_types.ex — with Ecto Postgrex.Types.define( MyApp.PostgresTypes, [Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(), json: Jason ) ``` -------------------------------- ### Locate Point on Line with ST_LineLocatePoint Source: https://context7.com/felt/geo_postgis/llms.txt Determine the fractional distance (0.0 to 1.0) along a line segment that is closest to a given point. ```elixir # Locate the closest point on a line to a given point (returns 0.0–1.0 fraction) from(road in Road, select: %{road: road.name, fraction: st_line_locate_point(road.geom, ^stop_point)} ) ``` -------------------------------- ### Force Geometry to 2D with ST_Force2D Source: https://context7.com/felt/geo_postgis/llms.txt Convert a 3D or 4D geometry to its 2D representation by dropping Z and M coordinates. ```elixir # Force 2D (drop Z/M coordinates) from(l in Location3D, select: st_force_2d(l.geom)) ``` -------------------------------- ### Cluster Geometries with ST_ClusterKMeans Source: https://context7.com/felt/geo_postgis/llms.txt Assigns cluster IDs to geometries using the K-Means algorithm as a window function. Supports specifying the number of clusters and optionally a maximum radius (PostGIS 3.2+). Requires importing Geo.PostGIS and Ecto.Query. ```elixir import Geo.PostGIS import Ecto.Query # ST_ClusterKMeans — assigns a cluster ID (0-based) to each row using a window function from(l in Location, select: %{ id: l.id, name: l.name, cluster_id: st_cluster_k_means(l.geom, 5) |> over() } ) |> MyApp.Repo.all() # => [%{id: 1, name: "A", cluster_id: 0}, %{id: 2, name: "B", cluster_id: 2}, ...] # With max_radius (PostGIS 3.2+) — limits cluster radius in native units from(l in Location, select: %{ id: l.id, cluster_id: st_cluster_k_means(l.geom, 10, 1000.0) |> over() } ) # Cluster within a named window partition from(l in Location, windows: [w: [partition_by: l.category]], select: %{ name: l.name, category: l.category, cluster_id: st_cluster_k_means(l.geom, 3) |> over(:w) } ) ``` -------------------------------- ### Insert 3D Point using Postgrex Source: https://context7.com/felt/geo_postgis/llms.txt Insert a Geo.PointZ struct into a 'locations' table using a bare Postgrex connection. The struct is automatically encoded to WKB. ```elixir # Insert a 3D point point_z = %Geo.PointZ{coordinates: {-122.4194, 37.7749, 50.0}, srid: 4326} Postgrex.query!(pid, "INSERT INTO locations (name, geom) VALUES ($1, $2)", ["SF", point_z]) ``` -------------------------------- ### Generate Random Points with ST_GeneratePoints Source: https://context7.com/felt/geo_postgis/llms.txt Create a specified number of random points within a given polygon. An optional seed can be provided for reproducible results. ```elixir # Generate N random points within a polygon from(r in Region, select: st_generate_points(r.geom, 100)) from(r in Region, select: st_generate_points(r.geom, 100, 42)) # with seed ``` -------------------------------- ### Calculate Bounding Box Center Source: https://context7.com/felt/geo_postgis/llms.txt Combines `st_centroid` with coordinate accessors (`st_x`, `st_y`) to find the center coordinates of a geometry's bounding box. Requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # Combine with arithmetic for bounding box center from(r in Region, select: %{ center_x: st_x(st_centroid(r.geom)), center_y: st_y(st_centroid(r.geom)) }) ``` -------------------------------- ### Interpolate Point on Line with ST_LineInterpolatePoint Source: https://context7.com/felt/geo_postgis/llms.txt Find a point along a line segment at a specified fraction of its length. ```elixir # Interpolate a point at 50% along a line from(road in Road, select: st_line_interpolate_point(road.geom, 0.5)) ``` -------------------------------- ### Check Spatial Relationship (ST_Covers) Source: https://context7.com/felt/geo_postgis/llms.txt Verifies if one geometry covers every point of another geometry, including boundaries. Imports for Geo.PostGIS and Ecto.Query are necessary. ```elixir import Geo.PostGIS import Ecto.Query # ST_Covers — r.geom covers every point of search_point (no boundary exception) from(r in Region, where: st_covers(r.geom, ^search_point), select: r.id) ``` -------------------------------- ### Add Advanced Geometry Column with Projection in Ecto Migration Source: https://github.com/felt/geo_postgis/blob/master/README.md Use `execute` to add geometry columns with specific types like `geometry(Point,4326)` for standard GPS coordinates (longitude, latitude) and projection. ```elixir defmodule Repo.Migrations.AdvancedInit do use Ecto.Migration def change do create table(:test) do add :name, :string end # Add a field `lng_lat_point` with type `geometry(Point,4326)`. # This can store a "standard GPS" (epsg4326) coordinate pair {longitude,latitude}. execute("SELECT AddGeometryColumn ('test','lng_lat_point',4326,'POINT',2);", "") # Once a GIS data table exceeds a few thousand rows, you will want to build an index to speed up spatial searches of the data # Syntax - CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometryfield] ); execute("CREATE INDEX test_geom_idx ON test USING GIST (lng_lat_point);", "") end end ``` -------------------------------- ### Convert to Multi-Geometry with ST_Multi Source: https://context7.com/felt/geo_postgis/llms.txt Convert a single geometry (like a Point or LineString) into its corresponding Multi-geometry type (e.g., MultiPoint, MultiLineString). ```elixir # Wrap in Multi-* type from(l in Location, select: st_multi(l.geom)) ``` -------------------------------- ### Distance Functions Source: https://context7.com/felt/geo_postgis/llms.txt Compute planar or spherical distances and proximity filters between geometries. Functions like `st_distance`, `st_distance_in_meters`, `st_distancesphere`, `st_dwithin`, and `st_dwithin_in_meters` are available. ```APIDOC ## Distance Functions Compute planar or spherical distances and proximity filters between geometries. ```elixir import Geo.PostGIS import Ecto.Query # Planar distance in the geometry's native units (degrees if SRID 4326) from(l in Location, select: st_distance(l.geom, ^%Geo.Point{coordinates: {0.0, 0.0}, srid: 4326})) # Distance in meters — casts both arguments to geography internally from(l in Location, order_by: st_distance_in_meters(l.geom, ^origin), limit: 10, select: %{name: l.name, dist_m: st_distance_in_meters(l.geom, ^origin)}) # Sphere distance (fast approximation) from(l in Location, select: st_distancesphere(l.geom, ^origin)) # ST_DWithin — geometry units (filter rows within 0.01 degrees) from(l in Location, where: st_dwithin(l.geom, ^search_point, 0.01), select: l) # ST_DWithin — geography units (meters), with spheroid option search_point = %Geo.Point{coordinates: {-73.9857, 40.7484}, srid: 4326} from(l in Location, where: st_dwithin(l.geom, ^search_point, 500.0, true), # 500 m, use spheroid select: l.name) # ST_DWithin in meters (auto-casts to geography) from(l in Location, where: st_dwithin_in_meters(l.geom, ^search_point, 1000.0), select: l) ``` ``` -------------------------------- ### Read Back Geometry using Postgrex Source: https://context7.com/felt/geo_postgis/llms.txt Read back a geometry column using a bare Postgrex connection. The WKB binary data is automatically decoded into the corresponding Geo.* struct. ```elixir # Read back — result is automatically decoded to the matching Geo.* struct %{rows: [[%Geo.PointZ{coordinates: {lng, lat, alt}}]]} = Postgrex.query!(pid, "SELECT geom FROM locations WHERE name = $1", ["SF"]) ``` -------------------------------- ### Aggregate Bounding Box with ST_Extent Source: https://context7.com/felt/geo_postgis/llms.txt Calculate the aggregate bounding box that encompasses all geometries within a group. ```elixir # ST_Extent — aggregate bounding box of all geometries from(l in Location, select: st_extent(l.geom)) ``` -------------------------------- ### Extract X and Y Coordinates Source: https://context7.com/felt/geo_postgis/llms.txt Extracts the X (longitude) and Y (latitude) coordinates from point geometries. This requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # Extract X (longitude) and Y (latitude) from point columns from(l in Location, select: %{name: l.name, lon: st_x(l.geom), lat: st_y(l.geom)}) ``` -------------------------------- ### Compute Concave Hull with ST_ConcaveHull Source: https://context7.com/felt/geo_postgis/llms.txt Generate a concave hull for a geometry, allowing for more complex shapes than a convex hull. The 'pctconvex' parameter controls concavity, and 'allow_holes' can be set to true. ```elixir # Concave hull (pctconvex 0.0 = most concave, 1.0 = convex hull) from(r in Region, select: st_concave_hull(r.geom, 0.85)) from(r in Region, select: st_concave_hull(r.geom, 0.85, true)) # allow holes ``` -------------------------------- ### Compute Convex Hull with ST_ConvexHull Source: https://context7.com/felt/geo_postgis/llms.txt Calculate the smallest convex polygon that encloses a given geometry. ```elixir # Convex hull from(r in Region, select: st_convex_hull(r.geom)) ``` -------------------------------- ### Reproject Geometry with ST_Transform Source: https://context7.com/felt/geo_postgis/llms.txt Transform a geometry from one spatial reference system (SRID) to another. ```elixir # Reproject from SRID 4326 (WGS84) to 3857 (Web Mercator) from(l in Location, select: st_transform(l.geom, 3857)) ``` -------------------------------- ### Buffer Geometry with ST_Buffer Source: https://context7.com/felt/geo_postgis/llms.txt Create a polygon that represents an area within a specified distance of a geometry. The segment count can be adjusted for smoother curves. ```elixir # Buffer a point by 100 units (meters if projected SRS) from(l in Location, select: %{name: l.name, buffer: st_buffer(l.geom, 100.0)} ) ``` ```elixir # Buffer with segment count for smoother circles from(l in Location, select: st_buffer(l.geom, 500.0, 32)) ``` -------------------------------- ### Create Table with Geometry Column in Ecto Migration Source: https://github.com/felt/geo_postgis/blob/master/README.md Use the `:geometry` type when creating tables in Ecto migrations to add a spatial column. ```elixir defmodule Repo.Migrations.Init do use Ecto.Migration def change do create table(:test) do add :name, :string add :geom, :geometry end end end ``` -------------------------------- ### Cast GeometryCollection with SRID Source: https://context7.com/felt/geo_postgis/llms.txt Demonstrates casting a GeoJSON representation of a GeometryCollection, including its geometries and SRID, using Geo.PostGIS.Geometry.cast. ```elixir # Cast a GeometryCollection with SRID geo_json = %{ "type" => "GeometryCollection", "geometries" => [ %{"type" => "Point", "coordinates" => [0.0, 0.0]}, %{"type" => "LineString", "coordinates" => [[0.0, 0.0], [1.0, 1.0]]} ] } {:ok, %Geo.GeometryCollection{} = geom} = Geo.PostGIS.Geometry.cast(geo_json) ``` -------------------------------- ### Aggregate Geometries with ST_Collect and ST_MemUnion Source: https://context7.com/felt/geo_postgis/llms.txt Aggregates geometries into a single geometry collection or a multi-* type using ST_Collect. ST_MemUnion provides a memory-friendly alternative for aggregation union. Requires importing Geo.PostGIS and Ecto.Query. ```elixir import Geo.PostGIS import Ecto.Query # ST_Collect — aggregate all geoms into a GeometryCollection or Multi-* type from(l in Location, select: st_collect(l.geom)) # ST_Collect two geometries from(l in Location, select: st_collect(l.geom, ^other_geom)) # ST_MemUnion — memory-friendly aggregate union from(r in Region, group_by: r.category, select: {r.category, st_mem_union(r.geom)}) ``` -------------------------------- ### Use PostGIS ST_Distance Function in Ecto Query Source: https://github.com/felt/geo_postgis/blob/master/README.md Import `Geo.PostGIS` to use PostGIS functions like `st_distance` within Ecto queries. Ensure the `Location` schema and `Repo` are configured. ```elixir 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 end ``` -------------------------------- ### Define Postgrex Types with Geo.PostGIS.Extension Source: https://context7.com/felt/geo_postgis/llms.txt Define a Postgrex types module that includes the Geo.PostGIS.Extension. This allows Postgrex to handle PostGIS geometry and geography types. ```elixir # Define the types module (do this once at compile time, typically in a dedicated file) Postgrex.Types.define( MyApp.PostgresTypes, [Geo.PostGIS.Extension], decode_binary: :copy # :copy (default, safe) or :reference (faster, holds socket buffer) ) ``` -------------------------------- ### Aggregate Geometries with ST_Union Source: https://context7.com/felt/geo_postgis/llms.txt Compute the union of multiple geometries, either as an aggregate of all geometries in a group or the union of two specific geometries. ```elixir # Union of all geometries in a group (aggregate) from(l in Location, select: st_union(l.geom)) ``` ```elixir # Union of two geometries from(r in Region, select: st_union(r.geom, ^other_geom)) ``` -------------------------------- ### Count Points in LineString/Polygon with ST_NumPoints Source: https://context7.com/felt/geo_postgis/llms.txt Count the total number of vertices in a linestring or polygon. ST_NPoints is an alias that works on all geometry types. ```elixir # Number of points in a linestring or polygon from(road in Road, select: st_num_points(road.geom)) from(road in Road, select: st_n_points(road.geom)) # alias, works on all types ``` -------------------------------- ### Calculate Symmetric Difference with ST_SymDifference Source: https://context7.com/felt/geo_postgis/llms.txt Compute the symmetric difference between two geometries, returning the parts that are in either geometry but not in their intersection. ```elixir # ST_SymDifference from(r in Region, select: st_sym_difference(r.geom, ^other_geom)) ``` -------------------------------- ### Query GeoJSON Point from PostGIS Table Source: https://github.com/felt/geo_postgis/blob/master/README.md Retrieves data from the 'point_test' table, including the inserted GeoJSON point, using Postgrex. ```elixir Postgrex.Connection.query(pid, "SELECT * FROM point_test") {:ok, %Postgrex.Result{columns: ["id", "geom"], command: :select, num_rows: 1, rows: [{42, %Geo.Point{coordinates: {30.0, -90.0}, srid: 4326 }}]}} ``` -------------------------------- ### Insert Location by Casting GeoJSON String Source: https://context7.com/felt/geo_postgis/llms.txt Insert a location by providing a GeoJSON string for the geometry field, which will be cast by the Ecto type. ```elixir # Insert by casting GeoJSON string (e.g., from an HTTP API body) geoson = ~s({"type":"Point","coordinates":[2.2945,48.8584]}) %MyApp.Location{} |> MyApp.Location.changeset(%{name: "Eiffel Tower", geom: geojson}) |> MyApp.Repo.insert!() ``` -------------------------------- ### Insert Location with Geo Struct Source: https://context7.com/felt/geo_postgis/llms.txt Insert a new location record directly using a Geo.Point struct for the geometry field. ```elixir # Insert with a Geo struct directly %MyApp.Location{} |> MyApp.Location.changeset(%{ name: "Eiffel Tower", geom: %Geo.Point{coordinates: {2.2945, 48.8584}, srid: 4326} }) |> MyApp.Repo.insert!() ``` -------------------------------- ### Insert Location by Casting GeoJSON Map Source: https://context7.com/felt/geo_postgis/llms.txt Insert a location by providing an atom-keyed GeoJSON map for the geometry field, which will be cast by the Ecto type. ```elixir # Insert by casting an atom-keyed GeoJSON map %MyApp.Location{} |> MyApp.Location.changeset(%{ name: "Eiffel Tower", geom: %{type: "Point", coordinates: [2.2945, 48.8584]} }) |> MyApp.Repo.insert!() ``` -------------------------------- ### Check Spatial Relationship (ST_Relate) Source: https://context7.com/felt/geo_postgis/llms.txt Performs a detailed spatial relationship check using the DE-9IM matrix. Requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # ST_Relate — DE-9IM relationship matrix check from(l in Location, where: st_relate(l.geom, ^other_geom, "T*F**FFF2"), select: l) ``` -------------------------------- ### Check Geometry Validity Source: https://context7.com/felt/geo_postgis/llms.txt Filters out invalid geometries using the `st_is_valid` predicate. Ensure Geo.PostGIS and Ecto.Query are imported. ```elixir import Geo.PostGIS import Ecto.Query # Validity check — filter out invalid geometries from(r in Region, where: st_is_valid(r.geom), select: r) ``` -------------------------------- ### Ecto Schema with Geo.PostGIS.Geometry Source: https://context7.com/felt/geo_postgis/llms.txt Define an Ecto schema using Geo.PostGIS.Geometry for spatial columns. This type handles loading, dumping, and casting of Geo.* structs, including GeoJSON. ```elixir defmodule MyApp.Location do use Ecto.Schema import Ecto.Changeset schema "locations" do field :name, :string field :geom, Geo.PostGIS.Geometry timestamps() end def changeset(location, attrs) do location |> cast(attrs, [:name, :geom]) |> validate_required([:name, :geom]) end end ``` -------------------------------- ### Check Spatial Relationship (ST_Intersects) Source: https://context7.com/felt/geo_postgis/llms.txt Tests if two geometries intersect, meaning they share any common space. Requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # ST_Intersects — geometries share any space from(r in Region, where: st_intersects(r.geom, ^bounding_box), select: r) ``` -------------------------------- ### Calculate Difference with ST_Difference Source: https://context7.com/felt/geo_postgis/llms.txt Compute the geometric difference between two geometries, returning the parts of the first geometry that do not overlap with the second. ```elixir # Difference from(r in Region, select: st_difference(r.geom, ^clip_region)) ``` -------------------------------- ### Geometry Property Functions Source: https://context7.com/felt/geo_postgis/llms.txt Extract scalar properties such as area, length, SRID, geometry type, and validity from geometry columns. Functions include `st_area`, `st_length`, `st_srid`, `st_geometry_type`, `st_is_valid`, `st_make_valid`, `st_is_empty`, `st_dimension`, `st_is_collection`, and `st_mem_size`. ```APIDOC ## Geometry Property Functions Extract scalar properties (area, length, SRID, type, validity) from geometry columns. ```elixir import Geo.PostGIS import Ecto.Query # Area in native units (square degrees for SRID 4326, or square meters if projected) from(r in Region, select: %{name: r.name, area: st_area(r.geom)}) # Length of a line from(road in Road, order_by: [desc: st_length(road.geom)], limit: 5, select: road) # SRID from(l in Location, select: st_srid(l.geom)) # Geometry type as a string (e.g., "ST_Point", "ST_Polygon") from(l in Location, select: st_geometry_type(l.geom)) # Validity check — filter out invalid geometries from(r in Region, where: st_is_valid(r.geom), select: r) # Repair invalid geometries from(r in Region, update: [set: [geom: st_make_valid(r.geom)]], where: not st_is_valid(r.geom)) # st_make_valid with strategy parameters (PostGIS 3.1+) from(r in Region, select: st_make_valid(r.geom, "method=structure")) # Is empty, is collection, dimension from(l in Location, where: not st_is_empty(l.geom), select: %{dim: st_dimension(l.geom), is_coll: st_is_collection(l.geom)}) # Memory size in bytes from(l in Location, select: %{name: l.name, bytes: st_mem_size(l.geom)}) ``` ``` -------------------------------- ### Count Geometries in Collection with ST_NumGeometries Source: https://context7.com/felt/geo_postgis/llms.txt Determine the number of individual geometries contained within a multi-geometry object (e.g., MultiPolygon, GeometryCollection). ```elixir # Count sub-geometries in a collection from(r in Region, select: st_num_geometries(r.geom)) ``` -------------------------------- ### Decompose Geometry Collections with ST_Dump and ST_DumpGeometry Source: https://context7.com/felt/geo_postgis/llms.txt Decomposes geometry collections into individual rows using ST_Dump, returning a (path, geom) record. ST_DumpGeometry extracts only the geometry field, useful in subqueries. Requires importing Geo.PostGIS and Ecto.Query. ```elixir import Geo.PostGIS import Ecto.Query # ST_Dump — decompose a collection into (path, geom) record rows from(location in LocationMulti, select: st_dump(st_collect(location.geom))) # ST_DumpGeometry — extract only the geom field from a dump # Useful in subqueries to explode multi-geometries into individual rows dump_query = from(location in LocationMulti, select: %{geom: st_dump_geometry(st_collect(location.geom))}) from(polygons in subquery(dump_query), select: %{ geom: polygons.geom, point_count: st_num_points(polygons.geom) } ) |> MyApp.Repo.all() ``` -------------------------------- ### Topological Relationship Predicates Source: https://context7.com/felt/geo_postgis/llms.txt Boolean predicates for spatial relationships, used directly in `where` clauses. Includes functions like `st_within`, `st_contains`, `st_intersects`, `st_covers`, `st_crosses`, `st_relate`, `st_equals`, `st_disjoint`, `st_touches`, and `st_overlaps`. ```APIDOC ## Topological Relationship Predicates Boolean predicates for spatial relationships, used directly in `where` clauses. ```elixir import Geo.PostGIS import Ecto.Query city_boundary = %Geo.Polygon{coordinates: [[{...}, ...]], srid: 4326} # ST_Within — point is inside polygon from(l in Location, where: st_within(l.geom, ^city_boundary), select: l.name) # ST_Contains — polygon contains point from(r in Region, where: st_contains(r.geom, ^search_point), select: r.name) # ST_Intersects — geometries share any space from(r in Region, where: st_intersects(r.geom, ^bounding_box), select: r) # ST_Covers — r.geom covers every point of search_point (no boundary exception) from(r in Region, where: st_covers(r.geom, ^search_point), select: r.id) # ST_Crosses — geometries cross (e.g., road crosses river) from(road in Road, where: st_crosses(road.geom, ^river_line), select: road.name) # ST_Relate — DE-9IM relationship matrix check from(l in Location, where: st_relate(l.geom, ^other_geom, "T*F**FFF2"), select: l) # ST_Equals, ST_Disjoint, ST_Touches, ST_Overlaps from(l in Location, where: st_equals(l.geom, ^exact_geom), select: l.id) from(l in Location, where: st_disjoint(l.geom, ^region), select: l.name) from(l in Location, where: st_touches(l.geom, ^boundary), select: l) from(l in Location, where: st_overlaps(l.geom, ^other_area), select: l) ``` ``` -------------------------------- ### Calculate Distance in Meters Source: https://context7.com/felt/geo_postgis/llms.txt Calculates the distance in meters, automatically casting both geometries to 'geography' type for accurate spherical distance calculation. ```elixir import Geo.PostGIS import Ecto.Query # Distance in meters — casts both arguments to geography internally from(l in Location, order_by: st_distance_in_meters(l.geom, ^origin), limit: 10, select: %{name: l.name, dist_m: st_distance_in_meters(l.geom, ^origin)}) # ST_DWithin in meters (auto-casts to geography) from(l in Location, where: st_dwithin_in_meters(l.geom, ^search_point, 1000.0), select: l) ``` -------------------------------- ### Coordinate Accessors Source: https://context7.com/felt/geo_postgis/llms.txt Extract individual coordinate values (X, Y, Z, M) from point geometries. Functions include `st_x`, `st_y`, `st_z`, and `st_m`. ```APIDOC ## Coordinate Accessors Extract individual coordinate values (X, Y, Z, M) from point geometries. ```elixir import Geo.PostGIS import Ecto.Query # Extract X (longitude) and Y (latitude) from point columns from(l in Location, select: %{name: l.name, lon: st_x(l.geom), lat: st_y(l.geom)}) # Extract Z (altitude) from PointZ column from(l in Location3D, select: %{name: l.name, alt: st_z(l.geom)}) # Extract M (measure) from PointM column from(l in LocationM, select: st_m(l.geom)) # Combine with arithmetic for bounding box center from(r in Region, select: %{ center_x: st_x(st_centroid(r.geom)), center_y: st_y(st_centroid(r.geom)) } ) ``` ``` -------------------------------- ### Calculate Intersection with ST_Intersection Source: https://context7.com/felt/geo_postgis/llms.txt Find the geometric intersection between two geometries. This requires joining geometries based on spatial intersection. ```elixir # Intersection of two geometries from(r in Region, join: s in Region, on: st_intersects(r.geom, s.geom) and r.id != s.id, select: %{intersection: st_intersection(r.geom, s.geom)} ) ``` -------------------------------- ### Set SRID with ST_SetSRID Source: https://context7.com/felt/geo_postgis/llms.txt Assign or override the Spatial Reference Identifier (SRID) for a geometry without changing its coordinates. ```elixir # Set/override SRID without reprojection from(l in Location, select: st_set_srid(l.geom, 4326)) ``` -------------------------------- ### Extract Nth Geometry with ST_GeometryN Source: https://context7.com/felt/geo_postgis/llms.txt Retrieve a specific sub-geometry from a multi-geometry object using a 1-based index. ```elixir # Extract the Nth sub-geometry (1-based index) from(r in Region, select: st_geometry_n(r.geom, 1)) ``` -------------------------------- ### Insert Polygon with Hole using Postgrex Source: https://context7.com/felt/geo_postgis/llms.txt Insert a Geo.Polygon struct with an inner hole into a 'regions' table using a bare Postgrex connection. The struct is automatically encoded to WKB. ```elixir # Insert a polygon with a hole polygon = %Geo.Polygon{ coordinates: [ [{0.0, 0.0}, {10.0, 0.0}, {10.0, 10.0}, {0.0, 10.0}, {0.0, 0.0}], [{2.0, 2.0}, {8.0, 2.0}, {8.0, 8.0}, {2.0, 8.0}, {2.0, 2.0}] ], srid: 4326 } Postgrex.query!(pid, "INSERT INTO regions (geom) VALUES ($1)", [polygon]) ``` -------------------------------- ### Calculate Centroid with ST_Centroid Source: https://context7.com/felt/geo_postgis/llms.txt Compute the geometric center (centroid) of a polygon or other geometry. ```elixir # Centroid of a polygon from(r in Region, select: %{name: r.name, centroid: st_centroid(r.geom)}) ``` -------------------------------- ### Calculate Sphere Distance Source: https://context7.com/felt/geo_postgis/llms.txt Computes a fast approximation of the distance between geometries on a sphere. ```elixir import Geo.PostGIS import Ecto.Query # Sphere distance (fast approximation) from(l in Location, select: st_distancesphere(l.geom, ^origin)) ``` -------------------------------- ### Define Ecto Schema with GeoPostGIS Geometry Source: https://github.com/felt/geo_postgis/blob/master/README.md Defines an Ecto schema for a table that includes a 'geom' field using the Geo.PostGIS.Geometry type. ```elixir defmodule Test do use Ecto.Schema schema "test" do field :name, :string field :geom, Geo.PostGIS.Geometry end end ``` -------------------------------- ### Extract M Coordinate Source: https://context7.com/felt/geo_postgis/llms.txt Extracts the M (measure) coordinate from a PointM geometry. Requires Geo.PostGIS and Ecto.Query imports. ```elixir import Geo.PostGIS import Ecto.Query # Extract M (measure) from PointM column from(l in LocationM, select: st_m(l.geom)) ```