lib/ash_geo.ex

defmodule AshGeo do
  @moduledoc """
  Base module containing common utility functions

  This module contains some things used internally that may also be useful
  outside of `AshGeo` itself, but depending upon what you what to do, you should
  probably be using one of:

  - `AshGeo.Validation` for validations of `Geo` types and using `Topo`
    functions
  - `AshGeo.Postgis` for [PostGIS `st_*`
    functions](https://postgis.net/docs/reference.html) for use with `Ash.Expr`
  - `AshGeo.GeoAny`, `AshGeo.GeoJson`, `AshGeo.GeoWkt`, `AshGeo.GeoWkb` for
    input types
  - `AshGeo.Geometry` for base geometry type
  """
  @moduledoc since: "0.1.0"

  @doc "Transform the last element of a module path into a snake-cased atom."
  @doc since: "0.1.0"
  # sobelow_skip ["DOS.StringToAtom"]
  def module_suffix_to_snake(module) do
    module
    |> Module.split()
    |> List.last()
    |> Macro.underscore()
    |> String.to_atom()
  end

  @doc "Macro to check whether a module is a `Geo` type, suitable for use in guards"
  @doc since: "0.1.0"
  # credo:disable-for-lines:17 Credo.Check.Refactor.CyclomaticComplexity
  defmacro is_geo(struct) do
    quote location: :keep do
      unquote(struct) == Geo.Point or
        unquote(struct) == Geo.PointZ or
        unquote(struct) == Geo.PointM or
        unquote(struct) == Geo.PointZM or
        unquote(struct) == Geo.LineString or
        unquote(struct) == Geo.LineStringZ or
        unquote(struct) == Geo.Polygon or
        unquote(struct) == Geo.PolygonZ or
        unquote(struct) == Geo.MultiPoint or
        unquote(struct) == Geo.MultiPointZ or
        unquote(struct) == Geo.MultiLineString or
        unquote(struct) == Geo.MultiLineStringZ or
        unquote(struct) == Geo.MultiPolygon or
        unquote(struct) == Geo.MultiPolygonZ or
        unquote(struct) == Geo.GeometryCollection
    end
  end

  @doc "List of `Geo` types"
  @doc since: "0.1.0"
  def geo_types do
    [
      Geo.Point,
      Geo.PointZ,
      Geo.PointM,
      Geo.PointZM,
      Geo.LineString,
      Geo.LineStringZ,
      Geo.Polygon,
      Geo.PolygonZ,
      Geo.MultiPoint,
      Geo.MultiPointZ,
      Geo.MultiLineString,
      Geo.MultiLineStringZ,
      Geo.MultiPolygon,
      Geo.MultiPolygonZ,
      Geo.GeometryCollection
    ]
  end

  @doc """
  Type aliases for `Geo` types, auto-generated from the module names

  For example, the alias derived from `Geo.PointZM` is `:point_zm`.
  """
  @doc since: "0.1.0"
  def geo_type_aliases do
    Enum.map(geo_types(), fn type ->
      {module_suffix_to_snake(type), type}
    end)
  end

  @doc "List of `Topo` functions"
  @doc since: "0.1.0"
  def topo_functions do
    [
      :contains?,
      :disjoint?,
      :equals?,
      :intersects?,
      :within?
    ]
  end
end