Current section
Files
Jump to
Current section
Files
lib/geo_sql/mm.ex
defmodule GeoSQL.MM do
@moduledoc """
Functions from the SQL/MM standard that can used in Ecto queries.
## Examples
defmodule Example do
import Ecto.Query
use GeoSQL.MM
def example_query(geom) do
from location in Location, limit: 5, select: MM.distance(location.geom, ^geom)
end
end
"""
defmacro __using__(_) do
quote do
require GeoSQL.MM
require GeoSQL.MM.ThreeD
require GeoSQL.MM.Topo
alias GeoSQL.MM
end
end
use GeoSQL.RepoUtils
@spec area(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Measurement"
defmacro area(geometry) do
quote do: fragment("ST_Area(?)", unquote(geometry))
end
@spec as_binary(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Well-Known Binary (WKB)"
defmacro as_binary(geometry) do
quote do: fragment("ST_AsBinary(?)", unquote(geometry))
end
@spec as_text(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro as_text(geometry) do
quote do: fragment("ST_AsText(?)", unquote(geometry))
end
@spec boundary(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro boundary(geometry) do
quote do: fragment("ST_Boundary(?)", unquote(geometry))
end
@spec buffer(GeoSQL.geometry_input(), radius :: number) :: GeoSQL.fragment()
@doc group: "Geometry Processing"
defmacro buffer(geometry, radius) do
quote do: fragment("ST_Buffer(?, ?)", unquote(geometry), unquote(radius))
end
@spec buffer(
GeoSQL.geometry_input(),
radius :: number,
num_quarters_or_params :: number | String.t()
) ::
GeoSQL.fragment()
@doc group: "Geometry Processing"
defmacro buffer(geometry, radius, num_quarters_or_params) do
quote do
fragment(
"ST_Buffer(?, ?, ?)",
unquote(geometry),
unquote(radius),
unquote(num_quarters_or_params)
)
end
end
@spec centroid(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Processing"
defmacro centroid(geometry) do
quote do: fragment("ST_Centroid(?)", unquote(geometry))
end
@spec contains(GeoSQL.geometry_input(), contains :: GeoSQL.geometry_input()) ::
GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro contains(geometry, contains) do
quote do: fragment("ST_Contains(?,?)", unquote(geometry), unquote(contains))
end
@spec convex_hull(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Processing"
defmacro convex_hull(geometry) do
quote do: fragment("ST_ConvexHull(?)", unquote(geometry))
end
@spec coord_dim(GeoSQL.geometry_input(), Ecto.Repo.t() | nil) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
@doc """
Note: this function takes an optional Ecto.Repo parameter due to
some backends implementing the non-standard ST_NDims rather than ST_CoordDim
"""
defmacro coord_dim(geometry, repo \\ nil) do
case RepoUtils.adapter(repo) do
Ecto.Adapters.SQLite3 ->
quote do: fragment("ST_NDims(?)", unquote(geometry))
_ ->
quote do: fragment("ST_CoordDim(?)", unquote(geometry))
end
end
@spec crosses(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro crosses(geometryA, geometryB) do
quote do: fragment("ST_Crosses(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec curve_n(GeoSQL.geometry_input(), index :: integer | GeoSQL.geometry_input()) ::
GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro curve_n(compound_curve, index) do
quote do: fragment("ST_CurveN(?,?)", unquote(compound_curve), unquote(index))
end
@spec curve_to_line(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro curve_to_line(curve) do
quote do: fragment("ST_CurveToLine(?)", unquote(curve))
end
@doc group: "Geometry Editors"
defmacro curve_to_line(curve, tolerance, tolerance_type \\ 0, flags \\ 0) do
quote do
fragment(
"ST_CurveToLine(?, ?, ?, ?)",
unquote(curve),
unquote(tolerance),
unquote(tolerance_type),
unquote(flags)
)
end
end
@spec difference(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Overlays"
defmacro difference(geometryA, geometryB) do
quote do: fragment("ST_Difference(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec dimension(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro dimension(geometry) do
quote do: fragment("ST_Dimension(?)", unquote(geometry))
end
@spec disjoint(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro disjoint(geometryA, geometryB) do
quote do: fragment("ST_Disjoint(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec distance(GeoSQL.geometry_input(), GeoSQL.geometry_input(), in_meters :: boolean) ::
GeoSQL.fragment()
@doc group: "Measurement"
defmacro distance(geometryA, geometryB, in_meters \\ false) do
if in_meters do
quote do
fragment(
"ST_Distance(?::geography, ?::geography)",
unquote(geometryA),
unquote(geometryB)
)
end
else
quote do: fragment("ST_Distance(?,?)", unquote(geometryA), unquote(geometryB))
end
end
@spec equals(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro equals(geometryA, geometryB) do
quote do: fragment("ST_Equals(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec end_point(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro end_point(geometry) do
quote do: fragment("ST_EndPoint(?)", unquote(geometry))
end
@spec envelope(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro envelope(geometry) do
quote do: fragment("ST_Envelope(?)", unquote(geometry))
end
@spec geom_collection_from_text(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro geom_collection_from_text(text, srid \\ 0) do
quote do: fragment("ST_GeomCollFromText(?, ?)", unquote(text), unquote(srid))
end
@doc group: "Well-Known Text (WKT)"
defmacro geom_from_text(text, srid \\ 0) do
quote do: fragment("ST_GeomFromText(?,?)", unquote(text), unquote(srid))
end
@spec geom_from_wkb(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Binary (WKB)"
defmacro geom_from_wkb(bytea, srid \\ 0) do
quote do: fragment("ST_GeomFromWKB(?, ?)", unquote(bytea), unquote(srid))
end
@spec geometry_n(GeoSQL.geometry_input(), index :: pos_integer) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro geometry_n(geometry, index) do
quote do: fragment("ST_GeometryN(?, ?)", unquote(geometry), unquote(index))
end
@spec geometry_type(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro geometry_type(geometry) do
quote do: fragment("ST_GeometryType(?)", unquote(geometry))
end
@spec gml_to_sql(geomgml :: GeoSQL.fragment() | String.t(), Ecto.Repo.t()) :: GeoSQL.fragment()
@doc group: "Data Formats"
defmacro gml_to_sql(geomgml, repo \\ nil) do
case RepoUtils.adapter(repo) do
Ecto.Adapters.SQLite3 ->
quote do: fragment("GeomFromGML(?)", unquote(geomgml))
_ ->
quote do: fragment("ST_GMLToSQL(?)", unquote(geomgml))
end
end
@spec gml_to_sql(
geomgml :: GeoSQL.fragment() | String.t(),
srid :: GeoSQL.fragment() | non_neg_integer,
Ecto.Repo.t()
) :: GeoSQL.fragment()
@doc group: "Data Formats"
defmacro gml_to_sql(geomgml, srid, repo) do
case RepoUtils.adapter(repo) do
Ecto.Adapters.SQLite3 ->
quote do: fragment("SetSRID(GeomFromGML(?), ?)", unquote(geomgml), unquote(srid))
_ ->
quote do: fragment("ST_GMLToSQL(?,?)", unquote(geomgml), unquote(srid))
end
end
@spec interior_ring_n(GeoSQL.geometry_input(), index :: pos_integer) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro interior_ring_n(geometry, index) do
quote do: fragment("ST_InteriorRingN(?, ?)", unquote(geometry), unquote(index))
end
@spec intersection(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Overlays"
defmacro intersection(geometryA, geometryB) do
quote do: fragment("ST_Intersection(?, ?)", unquote(geometryA), unquote(geometryB))
end
@spec intersects(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro intersects(geometryA, geometryB) do
quote do: fragment("ST_Intersects(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec is_closed(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro is_closed(geometry) do
quote do: fragment("ST_IsClosed(?)", unquote(geometry))
end
@doc group: "Geometry Accessors"
defmacro is_empty(geometry) do
quote do: fragment("ST_IsEmpty(?)", unquote(geometry))
end
@spec is_ring(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro is_ring(geometry) do
quote do: fragment("ST_IsRing(?)", unquote(geometry))
end
@spec is_simple(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro is_simple(geometry) do
quote do: fragment("ST_IsSimple(?)", unquote(geometry))
end
@spec is_valid(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Validation"
defmacro is_valid(geometry) do
quote do: fragment("ST_IsValid(?)", unquote(geometry))
end
@spec length(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Measurement"
defmacro length(geometry) do
quote do: fragment("ST_Length(?)", unquote(geometry))
end
@spec line_from_text(text :: String.t(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro line_from_text(text, srid \\ 0) do
quote do: fragment("ST_LineFromText(?,?)", unquote(text), unquote(srid))
end
@spec line_from_text(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Binary (WKB)"
defmacro linestring_from_wkb(wkb, srid \\ 0) do
quote do: fragment("ST_LineStringFromWKB(?,?)", unquote(wkb), unquote(srid))
end
@doc group: "Linear Referencing"
defmacro locate_along(geometry, measure) do
quote do
fragment("ST_LocateAlong(?,?)", unquote(geometry), unquote(measure))
end
end
@doc group: "Linear Referencing"
defmacro locate_between(geometry, measure_start, measure_end) do
quote do
fragment(
"ST_LocateBetween(?,?,?)",
unquote(geometry),
unquote(measure_start),
unquote(measure_end)
)
end
end
@spec m(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro m(geometry) do
quote do: fragment("ST_M(?)", unquote(geometry))
end
@spec m_point_from_text(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro m_point_from_text(text, srid \\ 0) do
quote do: fragment("ST_MPointFromText(?, ?)", unquote(text), unquote(srid))
end
@spec m_line_from_text(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro m_line_from_text(text, srid \\ 0) do
quote do: fragment("ST_MLineFromText(?, ?)", unquote(text), unquote(srid))
end
@spec m_poly_from_text(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro m_poly_from_text(text, srid \\ 0) do
quote do: fragment("ST_MPolyFromText(?, ?)", unquote(text), unquote(srid))
end
@doc group: "Geometry Accessors"
defmacro num_curves(curve) do
quote do: fragment("ST_NumCurves(?)", unquote(curve))
end
@spec num_geometries(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro num_geometries(geometry) do
quote do: fragment("ST_NumGeometries(?)", unquote(geometry))
end
@spec num_interior_rings(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro num_interior_rings(geometry) do
quote do: fragment("ST_NumInteriorRing(?)", unquote(geometry))
end
@doc group: "Geometry Accessors"
defmacro num_patches(geometry, repo \\ nil) do
case RepoUtils.adapter(repo) do
Ecto.Adapters.SQLite3 ->
quote do: fragment("NumGeometries(?)", unquote(geometry))
_ ->
quote do: fragment("ST_NumPatches(?)", unquote(geometry))
end
end
@spec num_points(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro num_points(geometry) do
quote do: fragment("ST_NumPoints(?)", unquote(geometry))
end
@spec ordering_equals(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro ordering_equals(geometryA, geometryB) do
quote do: fragment("ST_OrderingEquals(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec overlaps(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro overlaps(geometryA, geometryB) do
quote do: fragment("ST_Overlaps(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec patch_n(
GeoSQL.geometry_input(),
face_index :: number | GeoSQL.geometry_input(),
Ecto.Repo.t() | nil
) ::
GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro patch_n(geometry, face_index, repo \\ nil) do
case RepoUtils.adapter(repo) do
Ecto.Adapters.SQLite3 ->
quote do: fragment("ST_GeometryN(?,?)", unquote(geometry), unquote(face_index))
_ ->
quote do: fragment("ST_PatchN(?,?)", unquote(geometry), unquote(face_index))
end
end
@spec perimeter(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Measurement"
defmacro perimeter(geometry) do
quote do: fragment("ST_Perimeter(?)", unquote(geometry))
end
@spec perimeter(GeoSQL.geometry_input(), use_spheroid :: boolean | GeoSQL.geometry_input()) ::
GeoSQL.fragment()
@doc group: "Measurement"
defmacro perimeter(geometry, use_spheroid) do
quote do: fragment("ST_Perimeter(?,?)", unquote(geometry), unquote(use_spheroid))
end
@spec point(x :: number, y :: number) :: GeoSQL.fragment()
@doc group: "Geometry Constructors"
defmacro point(x, y) do
quote do: fragment("ST_Point(?, ?)", unquote(x), unquote(y))
end
@spec point_from_text(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro point_from_text(text, srid \\ 0) do
quote do: fragment("ST_PointFromText(?, ?)", unquote(text), unquote(srid))
end
@spec point_from_wkb(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Binary (WKB)"
defmacro point_from_wkb(bytea, srid \\ 0) do
quote do: fragment("ST_PointFromWKB(?, ?)", unquote(bytea), unquote(srid))
end
@spec point_n(GeoSQL.geometry_input(), index :: pos_integer) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro point_n(geometry, index) do
quote do: fragment("ST_PointN(?, ?)", unquote(geometry), unquote(index))
end
@spec point_on_surface(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Processing"
defmacro point_on_surface(geometry) do
quote do: fragment("ST_PointOnSurface(?)", unquote(geometry))
end
@doc group: "Geometry Constructors"
@doc "NOTE: this function does not currently exist in Spatialite"
defmacro polygon(linestring, srid) do
quote do: fragment("ST_Polygon(?,?)", unquote(linestring), unquote(srid))
end
@spec polygon_from_text(text :: binary(), srid :: integer) :: GeoSQL.fragment()
@doc group: "Well-Known Text (WKT)"
defmacro polygon_from_text(text, srid \\ 0) do
quote do: fragment("ST_PolygonFromText(?, ?)", unquote(text), unquote(srid))
end
@spec relate(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro relate(geometryA, geometryB) do
quote do: fragment("ST_Relate(?,?)", unquote(geometryA), unquote(geometryB))
end
@type boundary_node_rule :: :ogc_mod2 | :endpoint | :multivalent_endpoint | :monovalent_endpoint
@spec relate(
GeoSQL.geometry_input(),
GeoSQL.geometry_input(),
matrix_or_rule :: String.t() | boundary_node_rule
) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro relate(geometryA, geometryB, boundary_rule) when is_atom(boundary_rule) do
rule_value =
case boundary_rule do
:ogc_mod2 -> 1
:endpoint -> 2
:multivalent_endpoint -> 3
:monovalent_endpoint -> 4
end
quote do
fragment(
"ST_Relate(?,?,?)",
unquote(geometryA),
unquote(geometryB),
unquote(rule_value)
)
end
end
@doc group: "Topological Relationships"
defmacro relate(geometryA, geometryB, intersectionPatternMatrix) do
quote do
fragment(
"ST_Relate(?,?,?)",
unquote(geometryA),
unquote(geometryB),
unquote(intersectionPatternMatrix)
)
end
end
@spec srid(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Spatial Reference Systems"
defmacro srid(geometry) do
quote do: fragment("ST_SRID(?)", unquote(geometry))
end
@spec start_point(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro start_point(geometry) do
quote do: fragment("ST_StartPoint(?)", unquote(geometry))
end
@spec sym_difference(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Overlays"
defmacro sym_difference(geometryA, geometryB) do
quote do: fragment("ST_SymDifference(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec touches(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro touches(geometryA, geometryB) do
quote do: fragment("ST_Touches(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec transform(geometry :: GeoSQL.geometry_input(), srid :: pos_integer()) :: GeoSQL.fragment()
@doc group: "Affine Transforms"
defmacro transform(geometry, srid) do
quote do: fragment("ST_Transform(?, ?)", unquote(geometry), unquote(srid))
end
@spec union(geometryList :: GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Overlays"
defmacro union(geometryList) do
quote do: fragment("ST_Union(?)", unquote(geometryList))
end
@spec union(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Overlays"
defmacro union(geometryA, geometryB) do
quote do: fragment("ST_Union(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec within(GeoSQL.geometry_input(), GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Topological Relationships"
defmacro within(geometryA, geometryB) do
quote do: fragment("ST_Within(?,?)", unquote(geometryA), unquote(geometryB))
end
@spec x(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro x(geometry) do
quote do: fragment("ST_X(?)", unquote(geometry))
end
@spec y(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro y(geometry) do
quote do: fragment("ST_Y(?)", unquote(geometry))
end
@spec z(GeoSQL.geometry_input()) :: GeoSQL.fragment()
@doc group: "Geometry Accessors"
defmacro z(geometry) do
quote do: fragment("ST_Z(?)", unquote(geometry))
end
end