GeoSQL provides access to spatial data stored in databases such as PostGIS and Spatialite (SQLite3 extension for spatial data).
Spatial data is everywhere and mapping is a key feature in many applications. GeoSQL provides a portable and powerful toolbox for interacting with the spatial databases we store that data in.
Portable GIS functionality
GeoSQL provides access to hundreds of standard and non-standard spatial SQL functions to Ecto. This makes representing and interacting with spatial data easy in Ecto schemas and queries. Two-dimensional data as well as Z, M, and ZM variants are supported along with coordinate system support via SRIDs.
Functions are split out by standards and availability including GeoSQL.MM2
and GeoSQL.MM3
. Beyond standards, GeoSQL.Common
contains non-standard but widely implemented functions, and database-specific calls are also provided in e.g. the GeoSQL.PostGIS
modules. Extensive support for multi-dimensional and topological queries is included. This makes it easy to see in your code which standards and backends a given piece of code requires to function.
Database-specific type extensions are provided along with utilities to e.g. decode geometries as needed.
The underlying geometry library supports WKT, WKB, and GeoJSON standards for easy data interchange. Other formats such as GML and KML are also supported via SQL functions.
defmodule MayApp.SpatialData do
use Ecto.Schema
use GeoSQL.MM2
use GeoSQL.QueryUtils
schema "spatial_data" do
field(:name, :string)
field(:geom, GeoSQL.Geometry)
field(:point, GeoSQL.Geometry.Point)
field(:pointz, GeoSQL.Geometry.PointZ)
field(:linestring, GeoSQL.Geometry.LineString)
field(:multipointzm, GeoSQL.Geometry.MultiPointZM)
field(:polygon, GeoSQL.Geometry.Polygon)
field(:multipolygon, GeoSQL.Geometry.MultiPolygon)
end
def features(repo) do
from(s in __MODULE__, select: s.geom)
|> repo.all()
end
def boundaries(repo) do
from(s in __MODULE__, select: MM2.boundary(s.geom))
|> repo.all()
|> QueryUtils.decode_geometry(repo)
end
end
High-level workflows
GeoSQL also provides high-level functionality, such as generating Mapbox Vector Tiles directly from a PostGIS database with a single function call. This makes it trivial to, for example, create vector map tile servers with a simple Plug:
defmodule MapTiles.VectorTilePlug do
@behaviour Plug
import Plug.Conn
alias GeoSQL.PostGIS.VectorTiles
def init(opts), do: opts
def call(%Plug.Conn{method: "GET", params: %{"x" => x, "y" => y, "z" => z}} = conn, _opts) do
z = Toolbelt.to_integer(z)
x = Toolbelt.to_integer(x)
y = Toolbelt.to_integer(y)
layers = [
%VectorTiles.Layer{
name: "pois",
source: "nodes",
columns: %{geometry: :geom, id: :node_id, tags: :tags}
}
]
result = VectorTiles.generate(MapTiles.Repo, z, x, y, layers, "osm")
conn
|> put_resp_content_type("application/vnd.mapbox-vector-tile")
|> resp(200, result)
|> send_resp()
|> halt()
end
end
More workflows will be provided as the library develops.
Design Goals
- Ease: fast to get started and hide complexity where possible.
- Portability: schemas and queries that work across databases, hiding the many (often trivial) differences between them.
- Completeness: extensive support for GIS SQL functions, not only the most common ones.
- Clarity: functions organized by their availability and standards compliance.
- Utility: out-of-the-box support for complete worfklows, to make complex tasks simple.
- Reliability: unit tested and deployed in real-world applications.