postgis

package
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package postgis models the PostGIS spatial types as a small, catalog-driven value layer shared by the schema builder, the SQL compiler, and the postgis plugin.

The design mirrors how enums and composites are handled elsewhere: nothing here emits a whole statement. It answers three questions —

  • is this column spatial, and which flavour (geometry vs geography)?
  • how does a GraphQL input value become a single SQL parameter?
  • what SQL expression reads or transforms such a value?

Everything else (filter surface, ordering, derived fields) is assembled from these pieces by callers.

The parameterization invariant

PostGIS values are user data, so they must move through the parameter list exactly like any other value (see the FuzzFilter invariant: compiled SQL text is a pure function of the query *shape*). A geometry input is therefore always reduced to ONE text parameter plus a constructor whose shape depends only on the column type — never on the value:

ST_GeomFromGeoJSON($1)   -- GeoJSON object input
ST_GeomFromEWKT($1)      -- WKT / EWKT string input

The choice between those two constructors is made from the Go type of the input (object vs string), which is part of the query shape as far as the fuzzer is concerned only if the same query text is reused — so callers pass the constructor through Value.SQL, which folds the decision into the expression and keeps the value itself in a parameter.

Index

Constants

View Source
const TypeName = "GeoJSON"

TypeName is the GraphQL scalar every spatial column maps to. Values are GeoJSON objects on the way out and GeoJSON-or-WKT on the way in.

Variables

View Source
var Deriveds = []Derived{
	{
		Suffix: "Area", Type: "Float",
		Description: "Area of the geometry (ST_Area): square metres for geography, SRID units for geometry.",
		// contains filtered or unexported fields
	},
	{
		Suffix: "Length", Type: "Float",
		Description: "Length/perimeter of the geometry (ST_Length): metres for geography, SRID units for geometry.",
		// contains filtered or unexported fields
	},
	{
		Suffix: "Perimeter", Type: "Float",
		Description: "Perimeter of a polygonal geometry (ST_Perimeter).",
		// contains filtered or unexported fields
	},
	{
		Suffix: "Centroid", Type: TypeName, GeoJSON: true,
		Description: "Centroid of the geometry (ST_Centroid), as GeoJSON.",
		// contains filtered or unexported fields
	},
	{
		Suffix: "Envelope", Type: TypeName, GeoJSON: true,
		Description: "Bounding box of the geometry (ST_Envelope), as GeoJSON.",
		// contains filtered or unexported fields
	},
	{
		Suffix: "Srid", Type: "Int",
		Description: "Spatial reference identifier of the value (ST_SRID).",
		// contains filtered or unexported fields
	},
	{
		Suffix: "GeometryType", Type: "String",

		Description: "Geometry type name, e.g. ST_Point (ST_GeometryType).",
		// contains filtered or unexported fields
	},
	{
		Suffix: "IsValid", Type: "Boolean",
		Description: "Whether the geometry is topologically valid (ST_IsValid).",
		// contains filtered or unexported fields
	},
}

Deriveds are the derived accessors generated for every spatial column.

Each is a pure function of the column, so they compile into the same single statement as an ordinary column read — no extra round trip.

View Source
var Ops = []Op{
	{
		Name: "bboxIntersects", Arg: "BBoxInput", Index: true,
		Description: "Bounding boxes overlap (&&). Index-usable; the cheap first pass for viewport queries.",
		// contains filtered or unexported fields
	},
	{
		Name: "bboxContains", Arg: "BBoxInput", Index: true,
		Description: "This column's bounding box contains the given box (~).",
		// contains filtered or unexported fields
	},
	{
		Name: "bboxContainedBy", Arg: "BBoxInput", Index: true,
		Description: "This column's bounding box is contained by the given box (@).",
		// contains filtered or unexported fields
	},
	{
		Name: "intersects", Arg: TypeName, Index: true,
		Description: "Geometries share any portion of space (ST_Intersects).",
		// contains filtered or unexported fields
	},
	{
		Name: "disjoint", Arg: TypeName,
		Description: "Geometries share no space (ST_Disjoint). Cannot use an index.",
		// contains filtered or unexported fields
	},
	{
		Name: "contains", Arg: TypeName, Index: true,
		Description: "This column's geometry contains the given geometry (ST_Contains).",
		// contains filtered or unexported fields
	},
	{
		Name: "containsProperly", Arg: TypeName, Index: true,
		Description: "Contains with no boundary contact (ST_ContainsProperly).",
		// contains filtered or unexported fields
	},
	{
		Name: "within", Arg: TypeName, Index: true,
		Description: "This column's geometry lies within the given geometry (ST_Within).",
		// contains filtered or unexported fields
	},
	{
		Name: "covers", Arg: TypeName, Index: true,
		Description: "No point of the given geometry lies outside this column (ST_Covers).",
		// contains filtered or unexported fields
	},
	{
		Name: "coveredBy", Arg: TypeName, Index: true,
		Description: "No point of this column lies outside the given geometry (ST_CoveredBy).",
		// contains filtered or unexported fields
	},
	{
		Name: "crosses", Arg: TypeName, Index: true,
		Description: "Geometries cross (ST_Crosses).",
		// contains filtered or unexported fields
	},
	{
		Name: "overlaps", Arg: TypeName, Index: true,
		Description: "Geometries overlap at the same dimension (ST_Overlaps).",
		// contains filtered or unexported fields
	},
	{
		Name: "touches", Arg: TypeName, Index: true,
		Description: "Geometries touch at a boundary but interiors do not intersect (ST_Touches).",
		// contains filtered or unexported fields
	},
	{
		Name: "equals", Arg: TypeName,
		Description: "Geometries are spatially equal, ignoring vertex order (ST_Equals).",
		// contains filtered or unexported fields
	},
	{
		Name: "dwithin", Arg: "DWithinInput", Index: true,
		Description: "Within the given distance of the geometry (ST_DWithin). Distance is metres for geography, SRID units for geometry.",
		// contains filtered or unexported fields
	},
	{
		Name: "beyond", Arg: "DWithinInput",
		Description: "Further than the given distance from the geometry (ST_DWithin negated).",
		// contains filtered or unexported fields
	},
	{
		Name: "isNull", Arg: "Boolean",
		Description: "Test whether the column is null.",
		// contains filtered or unexported fields
	},
}

Ops is the spatial operator vocabulary exposed on every geometry and geography column.

Ordering matters only for schema readability: the index-usable bounding-box operators come first because they are the ones that stay fast on large tables, followed by the exact DE-9IM predicates.

Functions

func BBoxSQL

func BBoxSQL(minX, minY, maxX, maxY string, srid int, f Flavour) string

BBoxSQL renders a bounding box as ST_MakeEnvelope over four numeric parameters. The placeholders are supplied by the caller so the values stay in the parameter list; only the SRID (an integer read from the query shape) is inlined, matching how the rest of the compiler treats structural values.

func DerivedSuffixes

func DerivedSuffixes() []string

DerivedSuffixes returns the accessor suffixes in declaration order.

func DistanceSQL

func DistanceSQL(col, geom string) string

DistanceSQL renders the ordering expression for distance-from-a-point sorts.

func IsSpatial

func IsSpatial(pgType string) bool

IsSpatial is a convenience wrapper over FlavourOf.

func KNNSQL

func KNNSQL(col, geom string) string

KNNSQL renders the index-assisted nearest-neighbour operator (<->), which a GiST index can answer directly. Used for distance ordering because it is the operator PostGIS optimises for ORDER BY.

func OpNames

func OpNames() []string

OpNames returns the operator names in declaration order.

func OpsDoc

func OpsDoc() string

String renders the operator vocabulary for documentation/debugging.

func OutputSQL

func OutputSQL(ref string) string

OutputSQL renders a spatial column as the GeoJSON text the response carries.

ST_AsGeoJSON returns text, so the result is re-parsed into jsonb to embed it as a real JSON object rather than a JSON-encoded string. NULL propagates through both calls untouched.

func SimplifySQL

func SimplifySQL(ref, tolerance string, f Flavour) string

SimplifySQL renders the per-field `simplify:` argument: drop vertices under the given tolerance (ST_SimplifyPreserveTopology, which never produces an invalid geometry). The tolerance is a placeholder supplied by the caller.

func TransformSQL

func TransformSQL(ref string, srid int, f Flavour) string

TransformSQL renders the per-field `transform:` argument: reproject a value into another SRID. The SRID is an integer from the query shape, so inlining it keeps the value parameter list untouched.

Types

type ArgKind

type ArgKind int

ArgKind classifies the operand shape so callers know what to parse.

const (
	// ArgGeometry: the operand is a bare GeoJSON/WKT value.
	ArgGeometry ArgKind = iota
	// ArgDWithin: the operand is {geometry, distance}.
	ArgDWithin
	// ArgBBox: the operand is {minX, minY, maxX, maxY, srid}.
	ArgBBox
	// ArgBoolean: the operand is a Boolean (isNull).
	ArgBoolean
)

type Derived

type Derived struct {
	// Suffix is appended to the column's field name.
	Suffix string
	// Type is the SDL type of the generated field.
	Type string
	// Description documents the field.
	Description string
	// GeoJSON marks accessors returning a geometry (rendered as GeoJSON).
	GeoJSON bool
	// contains filtered or unexported fields
}

Derived is one computed spatial accessor exposed as a sibling field of a spatial column (e.g. `location` -> `locationArea`).

func DerivedBySuffix

func DerivedBySuffix(suffix string) (Derived, bool)

DerivedBySuffix looks up a derived accessor.

func (Derived) SQL

func (d Derived) SQL(ref string, f Flavour) string

SQL renders the derived accessor over a column reference, wrapping geometry valued results as GeoJSON.

type Flavour

type Flavour int

Flavour distinguishes the two spatial base types. They share an operator vocabulary but differ in units and in a few function names, so the compiler needs to keep them apart.

const (
	// NotSpatial marks a column PostGIS does not own.
	NotSpatial Flavour = iota
	// Geometry is planar: distances and areas are in SRID units.
	Geometry
	// Geography is spheroidal: distances and areas are in metres.
	Geography
)

func FlavourOf

func FlavourOf(pgType string) Flavour

FlavourOf classifies a canonical pg type name (array prefix already stripped by the caller, or not — both forms are accepted).

PostGIS installs its types into whichever schema the extension lives in, so classification is by type name rather than by schema: a `geometry` column is spatial whether the extension sits in public, postgis, or a tenant schema.

func (Flavour) MetresNative

func (f Flavour) MetresNative() bool

MetresNative reports whether distance/length arguments and results are in metres without an explicit cast. Geography is metres by definition; geometry is in whatever unit its SRID uses.

func (Flavour) Spatial

func (f Flavour) Spatial() bool

Spatial reports whether the flavour is one of the PostGIS base types.

func (Flavour) String

func (f Flavour) String() string

type Op

type Op struct {
	// Name is the GraphQL field name inside GeometryFilterOps.
	Name string
	// Arg is the SDL type of the operand.
	Arg string
	// Description documents the operator in the generated schema.
	Description string
	// Index reports whether the operator can use a GiST index directly.
	// Index-usable operators are documented as such so users can tell the
	// cheap ones from the exact-but-expensive ones.
	Index bool
	// contains filtered or unexported fields
}

Op describes one spatial filter operator: its GraphQL field name, the input shape it takes, and how it renders as SQL.

func OpByName

func OpByName(name string) (Op, bool)

OpByName looks up an operator by GraphQL field name.

func (Op) Kind

func (o Op) Kind() ArgKind

Kind reports the operand shape for an operator.

func (Op) Render

func (o Op) Render(col, geom string, extra ...string) string

Render builds the SQL predicate for an operator.

type Value

type Value struct {
	// Param is the single value handed to the driver: GeoJSON text or a
	// WKT/EWKT string.
	Param string

	// Flavour is the target type the expression is cast to.
	Flavour Flavour
	// SRID, when non-zero, is applied with ST_SetSRID. GeoJSON is always
	// WGS84 (4326) per RFC 7946 unless the value carries an explicit CRS;
	// EWKT carries its own SRID so none is forced.
	SRID int
	// contains filtered or unexported fields
}

Value is a spatial input value reduced to exactly one SQL parameter plus the constructor that turns it back into a PostGIS value.

func Parse

func Parse(v any, f Flavour) (Value, error)

Parse converts a GraphQL input value into a Value.

Two input forms are accepted, both collapsing to a single parameter:

  • a GeoJSON object ({"type":"Point","coordinates":[1,2]}), including Feature / FeatureCollection unwrapping, re-encoded as text;
  • a WKT or EWKT string ("POINT(1 2)", "SRID=4326;POINT(1 2)").

The value is validated structurally so malformed input fails at compile time with a user-facing error rather than as a Postgres error mid-statement.

func (Value) SQL

func (v Value) SQL(placeholder string) string

SQL renders the value expression given the placeholder for Param (e.g. "$3"). The result is a well-typed geometry/geography expression.

The placeholder is the ONLY value-derived part; the rest of the string is fixed by the column's type, preserving the compiled-SQL-is-a-pure-function- of-query-shape invariant.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL