postgis

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package postgis makes PostGIS type-safe without hiding it.

It is opt-in. A project that does not import it never sees a spatial API, and the root ORM knows nothing about geometry — everything here composes through the one extension boundary that package exposes, so there is no second query compiler and no second expression model.

Two distinctions run through the whole package and are never blurred:

geometry   Cartesian, in whatever units the SRID's coordinate system uses
geography  on the spheroid, with distances and lengths in metres

They are different PostgreSQL types with different index behaviour and different answers, so they are different Go types here. Converting between them is something you write, not something that happens to you.

the shape    Point, LineString, Polygon, and the multi forms
the SRID     which coordinate system the numbers are in

Both travel with the value and with the column, because losing either is how a query comes to compare metres with degrees and get a number back.

Index

Constants

View Source
const UnknownSRID int32 = 0

UnknownSRID is PostGIS's "no spatial reference": SRID 0.

It is not 4326. A geometry that never had an SRID assigned is in an unspecified coordinate system, and treating it as longitude and latitude because that is the common case is how coordinates end up in the wrong place on a map. Nothing here infers one.

Variables

View Source
var ErrShortEWKB = errors.New("postgis: the geometry data ends in the middle of a value")

ErrShortEWKB reports an encoding that ended before the geometry it described did.

Functions

func Collect

func Collect[E any](g GeomExpr[E]) orm.Agg[E, *Geometry]

Collect gathers the group's geometries into one multi geometry or collection, without dissolving the boundaries between them.

It is much cheaper than UnionAgg and answers a different question: Collect puts a hundred polygons in one value, Union merges them into the shape they jointly cover. Reach for Collect when the parts still matter.

The result's shape depends on what went in — points give a MultiPoint, mixed shapes give a GeometryCollection — so it claims none.

func DimConst

func DimConst(d Dim) string

DimConst names the exported Dim constant, for a generator writing Go.

func Extent

func Extent[E any](g GeomExpr[E]) orm.Agg[E, *Box2D]

Extent is the bounding box of every geometry in the group.

It returns a Box2D and not a geometry, because that is what PostGIS returns: ST_Extent's result type is box2d, a type with its own text form, no SRID and no binary send function. Typing it as a geometry would put the EWKB codec in front of bytes that are not EWKB.

The box carries no coordinate system. Use Box2D.Geometry with the SRID the group was in when a geometry is what is wanted.

func Extent3D

func Extent3D[E any](g GeomExpr[E]) orm.Agg[E, *Box3D]

Extent3D is Extent over three dimensions, returning a box3d.

func KindConst

func KindConst(k Kind) string

KindConst names the exported Kind constant, for a generator writing Go.

func Register

func Register(ctx context.Context, conn *pgx.Conn) error

Register teaches a connection the PostGIS types.

Wire it into a pool so every connection is taught once:

cfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
    return err
}
cfg.AfterConnect = postgis.Register
pool, err := pgxpool.NewWithConfig(ctx, cfg)

A project that also has generated enum registration composes the two:

cfg.AfterConnect = func(ctx context.Context, c *pgx.Conn) error {
    if err := domain.RegisterTypes(ctx, c); err != nil {
        return err
    }
    return postgis.Register(ctx, c)
}

It reports an error when PostGIS is not installed in the database, because a program that registers spatial types and then finds none is a program whose next query would fail with something much less specific.

func RegisterIfPresent

func RegisterIfPresent(ctx context.Context, conn *pgx.Conn) (bool, error)

RegisterIfPresent is Register for a program that runs against databases with and without PostGIS.

It reports whether the extension was found. When it was not, the connection is left exactly as it was and no error is returned — which is the right shape for an application whose spatial features are optional, and the wrong shape for one whose queries are all spatial.

func UnionAgg

func UnionAgg[E any](g GeomExpr[E]) orm.Agg[E, *Geometry]

UnionAgg merges the group's geometries into the single shape they cover, dissolving shared boundaries.

This is the aggregate ST_Union, which is a different function from the two-argument GeomExpr.Union despite the shared name — PostgreSQL tells them apart by arity and this package tells them apart by which type they hang on.

It is expensive. Merging a large group is a real geometric computation, and a query that does it per row of a join will be slow for reasons no index fixes.

Types

type Box2D

type Box2D struct {
	// The corners of the rectangle, in the reference system of whatever
	// produced it. Min is the lower-left, Max the upper-right.
	MinX, MinY float64
	MaxX, MaxY float64
	// Valid distinguishes a box from the absence of one. ST_Extent over no rows
	// is NULL, and a zero-size box at the origin is a different answer.
	Valid bool
}

Box2D is a rectangle in the plane, as PostGIS's box2d.

func (Box2D) Geometry

func (b Box2D) Geometry(srid int32) Geometry

Geometry returns the box as a closed polygon in the given SRID.

The SRID is an argument because a box does not carry one — see the note at the top of this file. A degenerate box, one with no width or no height, still produces the five-vertex ring PostGIS's own ST_Envelope would for a line or a point, rather than a shape that is not a polygon.

func (Box2D) Height

func (b Box2D) Height() float64

Height is the box's extent along Y.

func (Box2D) String

func (b Box2D) String() string

String renders the box the way PostGIS does.

func (Box2D) Width

func (b Box2D) Width() float64

Width is the box's extent along X.

type Box3D

type Box3D struct {
	// The corners of the box, in the reference system of whatever produced it.
	MinX, MinY, MinZ float64
	MaxX, MaxY, MaxZ float64
	// Valid distinguishes a box from the absence of one: ST_3DExtent over no
	// rows is NULL, and a zero-size box at the origin is a different answer.
	Valid bool
}

Box3D is a box in space, as PostGIS's box3d.

func (Box3D) String

func (b Box3D) String() string

String renders the box the way PostGIS does.

type Coord

type Coord struct {
	// X and Y are the horizontal ordinates — longitude and latitude in a
	// geographic reference system, easting and northing in a projected one.
	// Which they are is decided by the SRID, not by this type.
	//
	// Z is elevation and M is a measure: a per-vertex value, most often a
	// distance along a route or a timestamp. Both are zero when the geometry
	// does not carry them, which is why the dimension is recorded separately
	// rather than inferred from these being zero.
	X, Y, Z, M float64
}

Coord is one position.

Z and M are meaningful only when the geometry's dimensionality says so, which is why they are read through the geometry rather than tested for zero: 0 is a legitimate elevation.

type Dim

type Dim uint8

Dim is a geometry's coordinate dimensionality.

PostGIS stores four: the plain XY, XY with a Z ordinate, XY with a measure, and both. They are part of the type — geometry(PointZ,4326) is not geometry(Point,4326) — so nothing here drops an ordinate to make a value fit.

const (
	// XY is two ordinates, which is what an unqualified geometry has.
	XY Dim = iota
	// XYZ adds a Z ordinate.
	XYZ
	// XYM adds a measure.
	XYM
	// XYZM has both.
	XYZM
)

The dimensionalities PostGIS stores.

func (Dim) HasM

func (d Dim) HasM() bool

HasM reports whether the dimensionality carries a measure.

func (Dim) HasZ

func (d Dim) HasZ() bool

HasZ reports whether the dimensionality carries a Z ordinate.

func (Dim) String

func (d Dim) String() string

String renders the dimensionality as PostGIS spells it in a type name: the empty string for XY, and Z, M or ZM as a suffix for the others.

type Family

type Family uint8

Family is which of the two PostgreSQL spatial types a column has.

const (
	// NotSpatial is the zero value: this is not a spatial type at all.
	NotSpatial Family = iota
	// FamilyGeometry is PostGIS's geometry: Cartesian, in the SRID's units.
	FamilyGeometry
	// FamilyGeography is PostGIS's geography: on the spheroid, in metres.
	FamilyGeography
)

The spatial storage families.

func (Family) String

func (f Family) String() string

String renders the family as PostgreSQL names the type.

type GeogCol

type GeogCol[E any] struct {
	orm.Col[E, Geography]
	// contains filtered or unexported fields
}

GeogCol is a NOT NULL geography column of entity E.

func NewGeogCol

func NewGeogCol[E any](src *orm.Source, name string, srid int32, kind Kind, dim Dim) GeogCol[E]

NewGeogCol returns a geography column descriptor.

func (GeogCol[E]) Area

func (c GeogCol[E]) Area() orm.Value[E, float64]

Area is the column's area in square metres on the spheroid.

func (GeogCol[E]) Dim

func (c GeogCol[E]) Dim() Dim

Dim reports the dimensionality the column's type constrains it to.

func (GeogCol[E]) Expr

func (c GeogCol[E]) Expr() GeogExpr[E]

Expr lifts the column into a spatial expression on the spheroid.

func (GeogCol[E]) Kind

func (c GeogCol[E]) Kind() Kind

Kind reports the shape the column's type constrains it to, or zero when its type accepts any.

func (GeogCol[E]) Length

func (c GeogCol[E]) Length() orm.Value[E, float64]

Length is the column's length in metres on the spheroid.

func (GeogCol[E]) SRID

func (c GeogCol[E]) SRID() int32

SRID reports the coordinate system the column is declared in, or UnknownSRID when its type does not constrain one.

func (GeogCol[E]) TypeMod

func (c GeogCol[E]) TypeMod() TypeMod

TypeMod returns what the column's declared type says it holds, which is what FromExpr needs when that column is projected through a derived table.

type GeogExpr

type GeogExpr[E any] struct {
	// contains filtered or unexported fields
}

GeogExpr is a geography-valued expression over entity E.

It is a separate type from GeomExpr and not a flag on it, so that handing a geography to a function that measures in the plane does not compile. The mistake it prevents is the expensive one: ST_Distance over geometry(4326) returns degrees, which is a number, which looks like an answer.

func ComposeGeog

func ComposeGeog[E any](g GeogExpr[E]) GeogExpr[orm.Composed]

ComposeGeog drops the entity tag from a geography expression.

func GeogFromExpr

func GeogFromExpr[E any](v orm.Selectable[E, Geography], mod TypeMod) GeogExpr[E]

GeogFromExpr is FromExpr for a geography.

func GeogFromExprNull

func GeogFromExprNull[E any](v orm.Optional[E, *Geography], mod TypeMod) GeogExpr[E]

GeogFromExprNull is GeogFromExpr for a projected geography that can be NULL.

func GeogFromText

func GeogFromText[E any](wkt string) GeogExpr[E]

GeogFromText parses WKT or EWKT into a geography.

PostGIS's geography parser takes the SRID from an EWKT prefix and otherwise assumes 4326, which is the one place in this package a default coordinate system is not this package's choice — it is the function's own documented behaviour, and the expression records 4326 so that later checks see what PostGIS will see.

func GeogValue

func GeogValue[E any](g Geography) GeogExpr[E]

GeogValue lifts a geography into an expression, as a bind parameter.

func OfGeog

func OfGeog[E any](c interface{ Expr() GeogExpr[E] }) GeogExpr[orm.Composed]

OfGeog lifts a geography column of any entity into a composed spatial expression.

func (GeogExpr[E]) Area

func (g GeogExpr[E]) Area() orm.Value[E, float64]

Area is the geography's area in square metres.

func (GeogExpr[E]) AreaNull

func (g GeogExpr[E]) AreaNull() orm.Value[E, *float64]

AreaNull is GeogExpr.Area over a geography that can be NULL.

func (GeogExpr[E]) AsEWKT

func (g GeogExpr[E]) AsEWKT() orm.Value[E, string]

AsEWKT renders the geography as PostGIS's extended WKT.

func (GeogExpr[E]) AsGeoJSON

func (g GeogExpr[E]) AsGeoJSON() orm.Value[E, string]

AsGeoJSON renders the geography as a GeoJSON geometry object, as text.

func (GeogExpr[E]) AsGeoJSONNull

func (g GeogExpr[E]) AsGeoJSONNull() orm.Value[E, *string]

AsGeoJSONNull is GeogExpr.AsGeoJSON over a geography that can be NULL.

func (GeogExpr[E]) AsGeometry

func (g GeogExpr[E]) AsGeometry() GeomExpr[E]

AsGeometry casts a geography expression to geometry, so that the plane operations apply. Distances over the result are in the SRID's units.

func (GeogExpr[E]) AsText

func (g GeogExpr[E]) AsText() orm.Value[E, string]

AsText renders the geography as WKT.

func (GeogExpr[E]) AsTextNull

func (g GeogExpr[E]) AsTextNull() orm.Value[E, *string]

AsTextNull is GeogExpr.AsText over a geography that can be NULL.

func (GeogExpr[E]) BBoxIntersects

func (g GeogExpr[E]) BBoxIntersects(other GeogExpr[E]) orm.Predicate[E]

BBoxIntersects builds the && operator over the geographies' bounding boxes on the sphere.

func (GeogExpr[E]) BufferMetres

func (g GeogExpr[E]) BufferMetres(metres float64) GeogExpr[E]

BufferMetres grows a geography by distance in metres.

PostGIS returns a geography here, having done the work on the spheroid, which is what makes "everything within 500 metres" a question with one answer rather than one per latitude.

func (GeogExpr[E]) CoveredBy

func (g GeogExpr[E]) CoveredBy(other GeogExpr[E]) orm.Predicate[E]

CoveredBy reports whether no point of the receiver lies outside the other geography.

func (GeogExpr[E]) Covers

func (g GeogExpr[E]) Covers(other GeogExpr[E]) orm.Predicate[E]

Covers reports whether no point of the other geography lies outside the receiver.

func (GeogExpr[E]) DWithin

func (g GeogExpr[E]) DWithin(other GeogExpr[E], metres float64) orm.Predicate[E]

DWithin reports whether the two geographies are within metres of one another.

Metres, on the spheroid, whatever the latitude — which is the whole reason to store a geography. The plane form of this takes the coordinate system's units and is a different question; see GeomExpr.DWithin.

func (GeogExpr[E]) DeclaredSRID

func (g GeogExpr[E]) DeclaredSRID() int32

DeclaredSRID reports the coordinate system the expression's result is in, which for a geography is almost always 4326.

func (GeogExpr[E]) Distance

func (g GeogExpr[E]) Distance(other GeogExpr[E]) orm.Value[E, float64]

Distance is the shortest distance between the two geographies, in metres.

func (GeogExpr[E]) DistanceNull

func (g GeogExpr[E]) DistanceNull(other GeogExpr[E]) orm.Value[E, *float64]

DistanceNull is GeogExpr.Distance where either side can be NULL.

func (GeogExpr[E]) Intersects

func (g GeogExpr[E]) Intersects(other GeogExpr[E]) orm.Predicate[E]

Intersects reports whether the two geographies share any point.

func (GeogExpr[E]) KNNDistance

func (g GeogExpr[E]) KNNDistance(other GeogExpr[E]) orm.Value[E, float64]

KNNDistance builds the <-> operator over geographies, which orders by distance on the sphere through a spatial index.

func (GeogExpr[E]) Length

func (g GeogExpr[E]) Length() orm.Value[E, float64]

Length is the geography's length in metres.

func (GeogExpr[E]) LengthNull

func (g GeogExpr[E]) LengthNull() orm.Value[E, *float64]

LengthNull is GeogExpr.Length over a geography that can be NULL.

func (GeogExpr[E]) Nullable

func (g GeogExpr[E]) Nullable() bool

Nullable reports whether reading the expression can produce SQL NULL.

func (GeogExpr[E]) Perimeter

func (g GeogExpr[E]) Perimeter() orm.Value[E, float64]

Perimeter is the length of a geography polygon's boundary, in metres.

func (GeogExpr[E]) SRID

func (g GeogExpr[E]) SRID() orm.Value[E, int32]

SRID is the coordinate system the stored geography is labelled with.

func (GeogExpr[E]) Value

func (g GeogExpr[E]) Value() orm.Value[E, Geography]

Value reads the expression's result as a geography.

func (GeogExpr[E]) ValueNull

func (g GeogExpr[E]) ValueNull() orm.Value[E, *Geography]

ValueNull reads the expression's result as a geography that may be NULL.

type Geography

type Geography struct {
	// contains filtered or unexported fields
}

Geography is a geometry on the spheroid.

It holds the same coordinates a Geometry does and is a different Go type, because PostgreSQL treats it as a different type and the difference is not cosmetic:

ST_Distance over geometry(4326)   degrees, and not a distance anyone wants
ST_Distance over geography(4326)  metres along the spheroid

Both compile, both return a float8, and only one of them answers "how far apart are these two places". Nothing in this package converts between them on your behalf; Geometry.AsGeography and Geography.AsGeometry are how the conversion is written down, and they are the only way it happens.

The coordinates are longitude then latitude, in that order, because that is the order PostGIS reads them in: X is longitude. A pair written the other way round compiles, stores and returns wrong answers.

func GeographyPoint

func GeographyPoint(lon, lat float64) Geography

GeographyPoint returns a point on the spheroid at the given longitude and latitude, in SRID 4326.

The argument order is the one PostGIS uses and the opposite of how a place is usually spoken: longitude first.

func NewGeography

func NewGeography(g Geometry) (Geography, error)

NewGeography is Geometry.AsGeography for a geometry built inline, which is the common case.

func (Geography) AppendEWKB

func (g Geography) AppendEWKB(dst []byte) []byte

AppendEWKB appends the value's EWKB encoding to dst.

func (Geography) AsGeometry

func (g Geography) AsGeometry() Geometry

AsGeometry reinterprets the geography's positions as plane coordinates.

The numbers are unchanged; what changes is that distances and areas over the result are in the SRID's units rather than in metres.

func (Geography) Coords

func (g Geography) Coords() []Coord

Coords returns every position, in order.

func (Geography) Dim

func (g Geography) Dim() Dim

Dim reports the dimensionality.

func (Geography) EWKB

func (g Geography) EWKB() []byte

EWKB returns the value's EWKB encoding, which is the same encoding a geometry with these coordinates has — on the wire the two types are identical, and only the column tells them apart.

func (Geography) Equal

func (g Geography) Equal(other Geography) bool

Equal reports structural equality, with the same meaning it has on Geometry.Equal: same shape, same coordinates, same SRID, and not the topological question ST_Equals answers.

func (Geography) Geometries

func (g Geography) Geometries() []Geometry

Geometries returns the members of a collection or multi value.

func (Geography) IsEmpty

func (g Geography) IsEmpty() bool

IsEmpty reports whether this is one of PostGIS's empty values, which is not the same as being NULL.

func (Geography) Kind

func (g Geography) Kind() Kind

Kind reports the shape.

func (Geography) NumPoints

func (g Geography) NumPoints() int

NumPoints reports how many positions the value holds.

func (Geography) SRID

func (g Geography) SRID() int32

SRID reports the spatial reference, which for a geography is almost always 4326.

func (Geography) String

func (g Geography) String() string

String renders the value for a person.

type GeomCol

type GeomCol[E any] struct {
	orm.Col[E, Geometry]
	// contains filtered or unexported fields
}

GeomCol is a NOT NULL geometry column of entity E.

func NewGeomCol

func NewGeomCol[E any](src *orm.Source, name string, srid int32, kind Kind, dim Dim) GeomCol[E]

NewGeomCol returns a geometry column descriptor. Generated code calls it.

srid, kind and dim come from the column's declared type — geometry(Point,4326) gives all three, plain geometry gives none — and are what the build-time checks read. Passing UnknownSRID and a zero Kind means "the column does not say", which switches the corresponding check off rather than assuming a default.

func (GeomCol[E]) Area

func (c GeomCol[E]) Area() orm.Value[E, float64]

Area is the column's area in the units of its coordinate system.

func (GeomCol[E]) Dim

func (c GeomCol[E]) Dim() Dim

Dim reports the dimensionality the column's type constrains it to.

func (GeomCol[E]) Expr

func (c GeomCol[E]) Expr() GeomExpr[E]

Expr lifts the column into a spatial expression, which is what the operations in this package compose over.

Every spatial method is on GeomExpr rather than repeated on each of the four descriptors, and this is the one step between them. It is a method rather than something implicit because the descriptors differ in exactly one way that matters — whether the column can be NULL — and this is where that fact is recorded.

func (GeomCol[E]) Kind

func (c GeomCol[E]) Kind() Kind

Kind reports the shape the column's type constrains it to, or zero when its type accepts any.

func (GeomCol[E]) Length

func (c GeomCol[E]) Length() orm.Value[E, float64]

Length is the column's length in the units of its coordinate system.

func (GeomCol[E]) SRID

func (c GeomCol[E]) SRID() int32

SRID reports the coordinate system the column is declared in, or UnknownSRID when its type does not constrain one.

func (GeomCol[E]) TypeMod

func (c GeomCol[E]) TypeMod() TypeMod

TypeMod returns what the column's declared type says it holds, which is what FromExpr needs when that column is projected through a derived table.

type GeomExpr

type GeomExpr[E any] struct {
	// contains filtered or unexported fields
}

GeomExpr is a geometry-valued expression over entity E.

It is what every geometry column, constructor and transformation produces, so operations chain: buffer a column, take the centroid of the result, ask whether that intersects something. The entity tag rides along, which is what keeps a predicate built from one table's columns out of another table's query.

It carries what it knows about itself. The SRID and the shape come from the column's declared type or from the constructor that produced it, and they are what the build-time checks read; both are permitted to be unknown, and an unknown one is not checked rather than assumed.

func Compose

func Compose[E any](g GeomExpr[E]) GeomExpr[orm.Composed]

Compose drops the entity tag from a spatial expression that already is one, which is what a chain of transformations produces.

func FromExpr

func FromExpr[E any](v orm.Selectable[E, Geometry], mod TypeMod) GeomExpr[E]

FromExpr lifts a geometry-valued expression this package did not build.

A derived table or a CTE that projects a geometry hands back an ordinary orm.Expression, because the ORM's own machinery has no reason to know about PostGIS. This is how such a column re-enters the spatial layer: the caller states what the projected geometry is, because the projection did not carry it — a CTE column's type is whatever the CTE selected, and no part of the query records that it happened to be a point in 4326.

That makes it a trust boundary of the same kind orm.RawValue is: nothing checks the claim, and a wrong one makes the build-time SRID check wrong rather than the query wrong. Pass the zero TypeMod to claim nothing, which switches those checks off and leaves the server as the only authority.

loc := orm.Named("location", orm.Of(Places.Location))
near := orm.CTE("near", orm.Rows(loc).From(Places.Source()))
postgis.FromExpr(orm.Ref(near, loc), Places.Location.TypeMod())

func FromExprNull

func FromExprNull[E any](v orm.Optional[E, *Geometry], mod TypeMod) GeomExpr[E]

FromExprNull is FromExpr for a projected geometry that can be NULL.

func GeomFromEWKB

func GeomFromEWKB[E any](ewkb []byte) GeomExpr[E]

GeomFromEWKB parses PostGIS's extended WKB.

It exists for bytes that arrived from somewhere else already encoded — a cache, a message queue, another system's export. A geometry this program holds needs none of it: Value sends the same encoding through the codec with no function call in the way.

func GeomFromGeoJSON

func GeomFromGeoJSON[E any](doc string) GeomExpr[E]

GeomFromGeoJSON parses a GeoJSON geometry object.

The document is a bind parameter. GeoJSON is defined in WGS 84 and PostGIS tags the result 4326, which is what the expression records.

func GeomFromGeoJSONExpr

func GeomFromGeoJSONExpr[E any](doc orm.Selectable[E, string]) GeomExpr[E]

GeomFromGeoJSONExpr is GeomFromGeoJSON where the document comes from a column — a jsonb column rendered to text, or a text column holding one.

func GeomFromText

func GeomFromText[E any](wkt string, srid int32) GeomExpr[E]

GeomFromText parses WKT into a geometry, in the given coordinate system.

The SRID is required rather than optional, for the same reason MakeEnvelope's is: PostGIS's one-argument form produces SRID 0, and a geometry in SRID 0 silently fails to relate to anything in a table that has one. Pass UnknownSRID deliberately when that really is what is meant.

Malformed WKT is PostGIS's error, unchanged. This package does not parse WKT and so has no second opinion about what is malformed.

func GeomFromTextExpr

func GeomFromTextExpr[E any](wkt orm.Selectable[E, string], srid int32) GeomExpr[E]

GeomFromTextExpr is GeomFromText where the text comes from a column.

func MakeEnvelope

func MakeEnvelope[E any](minX, minY, maxX, maxY float64, srid int32) GeomExpr[E]

MakeEnvelope builds the rectangle between two corners, which is what a map viewport is.

The SRID is required rather than optional. PostGIS's own two-argument form defaults to 0, and a viewport in SRID 0 intersects nothing in a 4326 table — a query that silently returns no rows rather than failing.

func MakePoint

func MakePoint[E any](x, y orm.Selectable[E, float64]) GeomExpr[E]

MakePoint builds ST_MakePoint from two expressions.

The result has no SRID, because ST_MakePoint does not assign one: PostGIS returns a geometry in SRID 0, and a package that quietly made it 4326 would put every constructed point somewhere on Earth by accident. Wrap it in GeomExpr.SetSRID to say which coordinate system the numbers are in.

The ordinates are ordinary value expressions, so a point can be built from two columns — which is what a table of latitudes and longitudes needs, and the reason this exists alongside the Go-side NewPoint.

func MakePointM

func MakePointM[E any](x, y, m orm.Selectable[E, float64]) GeomExpr[E]

MakePointM builds ST_MakePointM, which takes a measure rather than an elevation.

It is a different function in PostGIS rather than a fourth argument, because ST_MakePoint with three arguments means Z. Getting that wrong stores a measure where an elevation belongs and nothing complains.

func MakePointZ

func MakePointZ[E any](x, y, z orm.Selectable[E, float64]) GeomExpr[E]

MakePointZ builds ST_MakePoint with an elevation.

func Of

func Of[E any](c interface{ Expr() GeomExpr[E] }) GeomExpr[orm.Composed]

Of lifts a column of any entity into a composed spatial expression.

postgis.Intersects(postgis.Of(Parks.Area), postgis.Of(Roads.Path))

The entity tag is dropped and nothing else is: the SRID, the shape and the nullability travel through, so a composed query gets the same checks an entity query does.

func Value

func Value[E any](g Geometry) GeomExpr[E]

Value lifts a geometry into an expression, as a bind parameter.

The coordinates are never text. They cross as EWKB in the statement's parameter list, which is the same path every other value in this package takes and the reason there is no way to interpolate a coordinate into SQL.

The cast is not decoration: PostGIS overloads almost every function on geometry and geography, and an uncast parameter leaves PostgreSQL resolving the overload with nothing to go on — it picks the deprecated text form and fails on bytes that were never text.

func (GeomExpr[E]) Area

func (g GeomExpr[E]) Area() orm.Value[E, float64]

Area is the geometry's area in the units of its coordinate system.

Over geometry(4326) that is square degrees, which is not an area anybody wants: a square degree is not a fixed size. Cast to geography, or use a projected coordinate system, when square metres are meant.

func (GeomExpr[E]) AreaNull

func (g GeomExpr[E]) AreaNull() orm.Value[E, *float64]

AreaNull is GeomExpr.Area over a geometry that can be NULL.

func (GeomExpr[E]) AsBinary

func (g GeomExpr[E]) AsBinary() orm.Value[E, []byte]

AsBinary renders the geometry as OGC WKB, which carries no SRID.

func (GeomExpr[E]) AsBinaryNull

func (g GeomExpr[E]) AsBinaryNull() orm.Value[E, *[]byte]

AsBinaryNull is GeomExpr.AsBinary over a geometry that can be NULL.

func (GeomExpr[E]) AsEWKB

func (g GeomExpr[E]) AsEWKB() orm.Value[E, []byte]

AsEWKB renders the geometry as PostGIS's extended WKB, which carries the SRID.

It is the same encoding Geometry.EWKB produces and the codec sends, so a query needs it only when the bytes themselves are the result — a cache key, a checksum, a payload for something else that speaks EWKB.

func (GeomExpr[E]) AsEWKBNull

func (g GeomExpr[E]) AsEWKBNull() orm.Value[E, *[]byte]

AsEWKBNull is GeomExpr.AsEWKB over a geometry that can be NULL.

func (GeomExpr[E]) AsEWKT

func (g GeomExpr[E]) AsEWKT() orm.Value[E, string]

AsEWKT renders the geometry as PostGIS's extended WKT, which prefixes the SRID: SRID=4326;POINT(1 2).

func (GeomExpr[E]) AsEWKTNull

func (g GeomExpr[E]) AsEWKTNull() orm.Value[E, *string]

AsEWKTNull is GeomExpr.AsEWKT over a geometry that can be NULL.

func (GeomExpr[E]) AsGeoJSON

func (g GeomExpr[E]) AsGeoJSON() orm.Value[E, string]

AsGeoJSON renders the geometry as a GeoJSON geometry object.

It reads back as a string rather than as a parsed document, because that is what PostGIS returns: ST_AsGeoJSON's result type is text, not json and not jsonb. Claiming otherwise would put a jsonb codec in front of bytes the server never marked as one.

GeoJSON is defined in WGS 84, so a geometry in another coordinate system should be transformed before rendering — PostGIS does not do it and neither does this.

func (GeomExpr[E]) AsGeoJSONNull

func (g GeomExpr[E]) AsGeoJSONNull() orm.Value[E, *string]

AsGeoJSONNull is GeomExpr.AsGeoJSON over a geometry that can be NULL.

func (GeomExpr[E]) AsGeography

func (g GeomExpr[E]) AsGeography() GeogExpr[E]

AsGeography casts a geometry expression to geography, which is how a query asks for metres from a column stored in the plane.

It is written out rather than inferred, exactly as Geometry.AsGeography is: the cast changes what the numbers mean, and PostGIS's own implicit cast to 4326 is a guess this package does not make. The expression must already state its coordinate system.

func (GeomExpr[E]) AsText

func (g GeomExpr[E]) AsText() orm.Value[E, string]

AsText renders the geometry as WKT, which is the OGC's text form and carries no SRID.

Use GeomExpr.AsEWKT when the coordinate system has to survive the rendering, which for anything leaving the program it usually does.

func (GeomExpr[E]) AsTextNull

func (g GeomExpr[E]) AsTextNull() orm.Value[E, *string]

AsTextNull is GeomExpr.AsText over a geometry that can be NULL.

func (GeomExpr[E]) Azimuth

func (g GeomExpr[E]) Azimuth(other GeomExpr[E]) orm.Value[E, *float64]

Azimuth is the bearing from one point to another, in radians clockwise from north, and NULL when the two points are in the same place.

func (GeomExpr[E]) BBoxContains

func (g GeomExpr[E]) BBoxContains(other GeomExpr[E]) orm.Predicate[E]

BBoxContains builds the ~ operator: the receiver's bounding box contains the other's.

func (GeomExpr[E]) BBoxDistance

func (g GeomExpr[E]) BBoxDistance(other GeomExpr[E]) orm.Value[E, float64]

BBoxDistance builds the <#> operator: the distance between the two geometries' bounding boxes.

func (GeomExpr[E]) BBoxIntersects

func (g GeomExpr[E]) BBoxIntersects(other GeomExpr[E]) orm.Predicate[E]

BBoxIntersects builds the && operator: the two bounding boxes overlap.

It is the cheap test the exact predicates run first, exposed on its own because sometimes the box is the question — a viewport, a tile, a coarse filter before something expensive. It is not a substitute for [Intersects]: two geometries whose boxes overlap need not touch at all.

func (GeomExpr[E]) BBoxSame

func (g GeomExpr[E]) BBoxSame(other GeomExpr[E]) orm.Predicate[E]

BBoxSame builds the ~= operator: the two bounding boxes are identical.

func (GeomExpr[E]) BBoxWithin

func (g GeomExpr[E]) BBoxWithin(other GeomExpr[E]) orm.Predicate[E]

BBoxWithin builds the @ operator: the receiver's bounding box is contained by the other's.

func (GeomExpr[E]) Boundary

func (g GeomExpr[E]) Boundary() GeomExpr[E]

Boundary is the geometry's boundary: the ring of a polygon, the endpoints of a line.

func (GeomExpr[E]) Buffer

func (g GeomExpr[E]) Buffer(distance float64) GeomExpr[E]

Buffer grows the geometry by distance in every direction, in the units of its coordinate system.

The result's shape is deliberately unconstrained. A positive buffer of anything is a polygon or a multi-polygon, and a negative buffer of a polygon can collapse to an empty geometry — so the metadata claims nothing and the value reports what it is.

func (GeomExpr[E]) Centroid

func (g GeomExpr[E]) Centroid() GeomExpr[E]

Centroid is the geometry's centre of mass, which PostGIS guarantees is a point — so the result metadata says Point, and that is one of the few places it can.

The centroid of an empty geometry is an empty point, and the centroid of a NULL is NULL. They are different answers and both come back.

func (GeomExpr[E]) CollectionExtract

func (g GeomExpr[E]) CollectionExtract(k Kind) GeomExpr[E]

CollectionExtract pulls the members of one shape out of a collection, which is how the polygon part of an ST_Intersection result is isolated.

func (GeomExpr[E]) Contains

func (g GeomExpr[E]) Contains(other GeomExpr[E]) orm.Predicate[E]

Contains reports whether the receiver contains the other geometry entirely, with no point of the other outside it and at least one point inside.

A geometry does not contain a point that lies exactly on its boundary. That is ST_Covers, and the difference is the boundary — which is where the points that matter usually are.

func (GeomExpr[E]) ContainsProperly

func (g GeomExpr[E]) ContainsProperly(other GeomExpr[E]) orm.Predicate[E]

ContainsProperly reports whether the other geometry lies in the receiver's interior, touching no part of its boundary.

func (GeomExpr[E]) ConvexHull

func (g GeomExpr[E]) ConvexHull() GeomExpr[E]

ConvexHull is the smallest convex geometry containing this one.

Like GeomExpr.Envelope it can be a point, a line or a polygon depending on what it was given, so it claims no shape.

func (GeomExpr[E]) CoordDim

func (g GeomExpr[E]) CoordDim() orm.Value[E, int16]

CoordDim is how many ordinates each position carries: two, three or four.

It reads back as int16 because PostGIS returns smallint for this one and integer for every other counting function beside it. Claiming int32 would compile, scan and be wrong about the type the server sent — which is the reason every one of these is checked against pg_typeof rather than guessed from the name.

func (GeomExpr[E]) CoveredBy

func (g GeomExpr[E]) CoveredBy(other GeomExpr[E]) orm.Predicate[E]

CoveredBy reports whether no point of the receiver lies outside the other geometry. It is Covers with the operands the other way round.

func (GeomExpr[E]) Covers

func (g GeomExpr[E]) Covers(other GeomExpr[E]) orm.Predicate[E]

Covers reports whether no point of the other geometry lies outside the receiver.

This is the one that includes the boundary, and it is usually what somebody means by "contains": a point on the edge of a district is in the district.

func (GeomExpr[E]) Crosses

func (g GeomExpr[E]) Crosses(other GeomExpr[E]) orm.Predicate[E]

Crosses reports whether the two geometries pass through one another: a line crossing a polygon, or two lines meeting at a point.

func (GeomExpr[E]) DFullyWithin

func (g GeomExpr[E]) DFullyWithin(other GeomExpr[E], distance float64) orm.Predicate[E]

DFullyWithin reports whether every part of each geometry is within distance of every part of the other, which is a question about the farthest points rather than the nearest.

func (GeomExpr[E]) DWithin

func (g GeomExpr[E]) DWithin(other GeomExpr[E], distance float64) orm.Predicate[E]

DWithin reports whether the two geometries are within distance of one another, measured in the units of their coordinate system.

The units are the trap. Over geometry(4326) the distance is in degrees, and a degree is about 111 km at the equator and much less near the poles — so a query written with 1000 for "a kilometre" selects most of a continent. Use a geography, or a projected coordinate system, when the answer should be in metres; see GeogExpr.DWithin.

It is the predicate a GiST index accelerates best, because PostGIS expands the bounding box by the distance and searches that.

func (GeomExpr[E]) DeclaredDim

func (g GeomExpr[E]) DeclaredDim() Dim

DeclaredDim reports the dimensionality the expression is known to produce.

func (GeomExpr[E]) DeclaredKind

func (g GeomExpr[E]) DeclaredKind() Kind

DeclaredKind reports the shape the expression is known to produce, or zero when it may produce any.

func (GeomExpr[E]) DeclaredSRID

func (g GeomExpr[E]) DeclaredSRID() int32

DeclaredSRID reports the coordinate system the expression's result is in, or UnknownSRID when the expression does not say.

It is what the column's declared type or the constructor said, known while the query is being built — where GeomExpr.SRID asks the server what the stored geometry is actually labelled with, and is an expression rather than a number. A plain geometry column has no declared SRID and holds geometries that each have one.

func (GeomExpr[E]) Difference

func (g GeomExpr[E]) Difference(other GeomExpr[E]) GeomExpr[E]

Difference is the part of the receiver that is not in the other geometry.

func (GeomExpr[E]) Dimension

func (g GeomExpr[E]) Dimension() orm.Value[E, int32]

Dimension is the geometry's topological dimension: zero for a point, one for a line, two for a polygon.

func (GeomExpr[E]) Disjoint

func (g GeomExpr[E]) Disjoint(other GeomExpr[E]) orm.Predicate[E]

Disjoint reports whether the two geometries share no point.

PostGIS does not index this one: there is no bounding box that proves two geometries do not touch, so it is a full scan. Prefer Not(Intersects) when the query has an index to use.

func (GeomExpr[E]) Distance

func (g GeomExpr[E]) Distance(other GeomExpr[E]) orm.Value[E, float64]

Distance is the shortest distance between the two geometries, in the units of their coordinate system.

It is zero when they touch. Over geometry(4326) the answer is in degrees; see GeogExpr.Distance for metres.

func (GeomExpr[E]) DistanceNull

func (g GeomExpr[E]) DistanceNull(other GeomExpr[E]) orm.Value[E, *float64]

DistanceNull is GeomExpr.Distance where either geometry can be NULL, and is nullable because PostgreSQL's answer then is NULL rather than zero — a distinction that matters, because zero means "they touch".

func (GeomExpr[E]) Envelope

func (g GeomExpr[E]) Envelope() GeomExpr[E]

Envelope is the geometry's bounding box as a geometry.

It is not always a polygon, which is why the result claims no shape: the envelope of a point is a point, the envelope of a horizontal line is a line, and only the general case is a rectangle. A package that typed this Polygon would be wrong about two of the three.

func (GeomExpr[E]) EqualsGeom

func (g GeomExpr[E]) EqualsGeom(other GeomExpr[E]) orm.Predicate[E]

EqualsGeom reports whether the two geometries occupy the same space.

This is topological equality, which is not the same as being made of the same vertices: a line drawn backwards equals itself, and a polygon with a repeated point equals the one without. It is the question a database should answer, and Geometry.Equal in Go deliberately answers the other one.

The name is not Equals because the embedded column descriptor already has Eq, which is PostgreSQL's = — an exact comparison of the stored representation, and a different question again.

func (GeomExpr[E]) Force2D

func (g GeomExpr[E]) Force2D() GeomExpr[E]

Force2D drops the Z and M ordinates.

It is spelled out because it loses data. A column typed geometry(PointZ,4326) does not accept the result, and this package will not do it implicitly to make an assignment typecheck.

func (GeomExpr[E]) Force3D

func (g GeomExpr[E]) Force3D() GeomExpr[E]

Force3D adds a Z ordinate of zero where there is none.

func (GeomExpr[E]) GeometryType

func (g GeomExpr[E]) GeometryType() orm.Value[E, string]

GeometryType is the shape's name as PostGIS spells it: ST_Point, ST_Polygon, and so on, with the ST_ prefix that GeometryType does not have.

func (GeomExpr[E]) Intersection

func (g GeomExpr[E]) Intersection(other GeomExpr[E]) GeomExpr[E]

Intersection is the part the two geometries have in common.

The shape depends entirely on how they meet — two polygons crossing give a polygon, two polygons touching along an edge give a line, two touching at a corner give a point, and two that miss give an empty geometry — so the result claims none.

func (GeomExpr[E]) Intersects

func (g GeomExpr[E]) Intersects(other GeomExpr[E]) orm.Predicate[E]

Intersects reports whether the two geometries share any point at all.

It is the negation of ST_Disjoint and the predicate most spatial queries want. An empty geometry intersects nothing, including itself.

func (GeomExpr[E]) IsEmptyGeom

func (g GeomExpr[E]) IsEmptyGeom() orm.Predicate[E]

IsEmptyGeom reports whether the geometry has no points.

It is not a NULL test. A NULL column has no geometry; an empty geometry is a geometry that covers nothing, and the two travel different paths through every query. Ask IsNull for the other question — the nullable descriptors have it, and the non-nullable ones deliberately do not.

func (GeomExpr[E]) IsSimple

func (g GeomExpr[E]) IsSimple() orm.Predicate[E]

IsSimple reports whether the geometry has no anomalous points: a line that does not cross itself, a multi-point with no repeats.

func (GeomExpr[E]) IsValid

func (g GeomExpr[E]) IsValid() orm.Predicate[E]

IsValid reports whether the geometry is one PostGIS considers well formed: a polygon whose rings close and do not cross themselves, and so on.

An invalid geometry is stored happily and gives wrong answers to almost every predicate, so this is worth asking about data that came from somewhere else.

func (GeomExpr[E]) IsValidReason

func (g GeomExpr[E]) IsValidReason() orm.Value[E, string]

IsValidReason explains why a geometry is invalid, in PostGIS's words.

It returns "Valid Geometry" for a valid one rather than NULL, which is why the result is a plain string.

func (GeomExpr[E]) IsValidReasonNull

func (g GeomExpr[E]) IsValidReasonNull() orm.Value[E, *string]

IsValidReasonNull is GeomExpr.IsValidReason over a geometry that can be NULL.

func (GeomExpr[E]) KNNDistance

func (g GeomExpr[E]) KNNDistance(other GeomExpr[E]) orm.Value[E, float64]

KNNDistance builds the <-> operator, which is what an ORDER BY uses to find the nearest rows through a spatial index.

This is the one to reach for when the question is "the ten closest", because PostgreSQL can walk a GiST index in distance order and stop after ten. An ORDER BY ST_Distance cannot: it has to measure every row first.

q.OrderBy(places.Location.Expr().KNNDistance(here).Asc()).Limit(10)

The number it returns is a distance between the geometries' centroids in recent PostGIS and between their bounding boxes in older ones. Order by it; do not report it as the distance.

func (GeomExpr[E]) Length

func (g GeomExpr[E]) Length() orm.Value[E, float64]

Length is the geometry's length in the units of its coordinate system. It is zero for anything that is not a line.

func (GeomExpr[E]) LengthNull

func (g GeomExpr[E]) LengthNull() orm.Value[E, *float64]

LengthNull is GeomExpr.Length over a geometry that can be NULL.

func (GeomExpr[E]) M

func (g GeomExpr[E]) M() orm.Value[E, *float64]

M is the point's measure, with the same cases GeomExpr.Z has: NULL for a point carrying no measure, an error for something that is not a point.

func (GeomExpr[E]) MakeValid

func (g GeomExpr[E]) MakeValid() GeomExpr[E]

MakeValid repairs an invalid geometry.

The shape can change — a self-intersecting polygon becomes a multi-polygon, and a degenerate one can become a line — so the result claims none. Nothing calls this on your behalf: an invalid geometry is data somebody should look at, and quietly repairing it on the way through a query would hide that.

func (GeomExpr[E]) MaxDistance

func (g GeomExpr[E]) MaxDistance(other GeomExpr[E]) orm.Value[E, float64]

MaxDistance is the greatest distance between any point of one geometry and any point of the other.

func (GeomExpr[E]) Multi

func (g GeomExpr[E]) Multi() GeomExpr[E]

Multi wraps a geometry in its multi form, which is what a column typed geometry(MultiPolygon,4326) requires of a polygon.

func (GeomExpr[E]) Nullable

func (g GeomExpr[E]) Nullable() bool

Nullable reports whether reading the expression can produce SQL NULL.

func (GeomExpr[E]) NumGeometries

func (g GeomExpr[E]) NumGeometries() orm.Value[E, int32]

NumGeometries is how many members a collection or multi geometry has, and one for a simple geometry.

func (GeomExpr[E]) NumPoints

func (g GeomExpr[E]) NumPoints() orm.Value[E, int32]

NumPoints is how many positions the geometry holds, across every ring and every member.

func (GeomExpr[E]) Overlaps

func (g GeomExpr[E]) Overlaps(other GeomExpr[E]) orm.Predicate[E]

Overlaps reports whether the two geometries share some but not all of their points, and have the same dimension.

Two polygons that partly cover one another overlap. A polygon and a point do not, whatever their positions, because their dimensions differ.

func (GeomExpr[E]) Perimeter

func (g GeomExpr[E]) Perimeter() orm.Value[E, float64]

Perimeter is the length of a polygon's boundary, and zero for anything else.

func (GeomExpr[E]) PerimeterNull

func (g GeomExpr[E]) PerimeterNull() orm.Value[E, *float64]

PerimeterNull is GeomExpr.Perimeter over a geometry that can be NULL.

func (GeomExpr[E]) PointOnSurface

func (g GeomExpr[E]) PointOnSurface() GeomExpr[E]

PointOnSurface is a point guaranteed to lie on the geometry, which the centroid of a crescent is not.

func (GeomExpr[E]) Relate

func (g GeomExpr[E]) Relate(other GeomExpr[E], pattern string) orm.Predicate[E]

Relate builds ST_Relate against a DE-9IM intersection matrix pattern.

The pattern is nine characters describing how the interiors, boundaries and exteriors of the two geometries meet — the general form every named predicate above is a special case of. It is validated here rather than sent as written, because a pattern is a value from the caller and it reaches the server as a bind parameter either way; the check is so a typo fails with a message about the pattern instead of a PostGIS error about a matrix.

func (GeomExpr[E]) SRID

func (g GeomExpr[E]) SRID() orm.Value[E, int32]

SRID is the coordinate system the stored geometry is labelled with.

It is the server's answer rather than the column's declaration, which is why it exists: a plain geometry column can hold geometries in several coordinate systems, and this is how a query finds out which.

func (GeomExpr[E]) SetSRID

func (g GeomExpr[E]) SetSRID(srid int32) GeomExpr[E]

SetSRID labels the geometry with a coordinate system.

It changes the metadata and not one coordinate. A point at (1, 2) with SRID set to 3857 is still at (1, 2); it is now claimed to be 1 metre east and 2 metres north of the web-Mercator origin rather than 1 degree east and 2 degrees north of Greenwich, which is a different place on Earth for the same pair of numbers.

Use it when the coordinates are already right and the label is missing — after ST_MakePoint, or over a column somebody loaded without one. Use GeomExpr.Transform when the coordinates need to move.

func (GeomExpr[E]) Simplify

func (g GeomExpr[E]) Simplify(tolerance float64) GeomExpr[E]

Simplify removes vertices using Douglas-Peucker, keeping the result within tolerance of the original.

It can break topology: two polygons that shared a boundary may not afterwards, and a polygon can simplify into something invalid. That is the algorithm rather than an implementation detail, and GeomExpr.SimplifyPreserveTopology is the slower function that does not do it.

func (GeomExpr[E]) SimplifyPreserveTopology

func (g GeomExpr[E]) SimplifyPreserveTopology(tolerance float64) GeomExpr[E]

SimplifyPreserveTopology removes vertices while keeping the result valid and keeping components that would otherwise vanish.

It is not GeomExpr.Simplify with a flag: it is a different algorithm with a different cost and a different result, and the two are offered separately because choosing between them is a real decision.

func (GeomExpr[E]) SymDifference

func (g GeomExpr[E]) SymDifference(other GeomExpr[E]) GeomExpr[E]

SymDifference is the part of either geometry that is not in both.

func (GeomExpr[E]) Touches

func (g GeomExpr[E]) Touches(other GeomExpr[E]) orm.Predicate[E]

Touches reports whether the two geometries meet at their boundaries and share no interior point.

func (GeomExpr[E]) Transform

func (g GeomExpr[E]) Transform(srid int32) GeomExpr[E]

Transform reprojects the geometry into another coordinate system.

The coordinates move. PostGIS does the projection using the definitions in spatial_ref_sys, which is why nothing here computes one: a projection is a large body of geodesy that belongs in PROJ and not in an ORM.

It needs a source coordinate system to project from, so an expression whose SRID is unknown is refused rather than guessed at.

func (GeomExpr[E]) Union

func (g GeomExpr[E]) Union(other GeomExpr[E]) GeomExpr[E]

Union is the two geometries together, as one.

This is the two-argument ST_Union, which is a different function from the aggregate of the same name: this combines two values in a row, and UnionAgg combines every row of a group.

func (GeomExpr[E]) Value

func (g GeomExpr[E]) Value() orm.Value[E, Geometry]

Value reads the expression's result as a geometry.

func (GeomExpr[E]) ValueNull

func (g GeomExpr[E]) ValueNull() orm.Value[E, *Geometry]

ValueNull reads the expression's result as a geometry that may be NULL.

func (GeomExpr[E]) Within

func (g GeomExpr[E]) Within(other GeomExpr[E]) orm.Predicate[E]

Within reports whether the receiver lies entirely inside the other geometry. It is Contains with the operands the other way round.

func (GeomExpr[E]) X

func (g GeomExpr[E]) X() orm.Value[E, *float64]

X is the point's first ordinate.

It is NULL when the geometry is NULL, and an error when the geometry is not a point.

func (GeomExpr[E]) Y

func (g GeomExpr[E]) Y() orm.Value[E, *float64]

Y is the point's second ordinate, with the same two cases GeomExpr.X has.

func (GeomExpr[E]) Z

func (g GeomExpr[E]) Z() orm.Value[E, *float64]

Z is the point's elevation.

It is NULL when the geometry is NULL and when the geometry carries no Z — an XY or XYM point — and an error when the geometry is not a point.

type Geometry

type Geometry struct {
	// contains filtered or unexported fields
}

Geometry is any shape, with its dimensionality and spatial reference.

It is one Go type rather than seven because a shape is data rather than a type in PostGIS too: a geometry column declared without a modifier holds any of them, ST_Intersection returns whichever the answer is, and a Go type per shape would make those unrepresentable. What is typed here is the column and the expression; the value carries its own shape and reports it.

The coordinate storage is flat and the structure is carried beside it, which is what keeps a polygon with a thousand vertices one allocation rather than a thousand.

func DecodeEWKB

func DecodeEWKB(b []byte) (Geometry, error)

DecodeEWKB reads a geometry from its EWKB encoding.

It accepts either byte order and both the SRID-bearing and plain forms, since what arrives depends on which function produced it: ST_AsBinary drops the SRID and ST_AsEWKB keeps it. A geometry decoded from plain WKB has UnknownSRID, which is the truth about it rather than a default.

func EmptyPoint

func EmptyPoint(srid int32) Geometry

EmptyPoint returns POINT EMPTY, which is a point with no position.

func NewCollection

func NewCollection(srid int32, members ...Geometry) (Geometry, error)

NewCollection returns a geometry collection.

The members keep their own shapes. Their spatial references have to agree with the collection's, because a collection whose members disagree is not a value PostGIS can store.

func NewLineString

func NewLineString(srid int32, dim Dim, cs ...Coord) Geometry

NewLineString returns a line through the given positions.

func NewMultiLineString

func NewMultiLineString(srid int32, dim Dim, lines ...[]Coord) Geometry

NewMultiLineString returns several lines as one geometry.

func NewMultiPoint

func NewMultiPoint(srid int32, dim Dim, cs ...Coord) Geometry

NewMultiPoint returns several points as one geometry.

func NewMultiPolygon

func NewMultiPolygon(srid int32, dim Dim, polygons ...[][]Coord) Geometry

NewMultiPolygon returns several polygons as one geometry, each given as its list of rings.

func NewPoint

func NewPoint(srid int32, x, y float64) Geometry

NewPoint returns a two-dimensional point.

func NewPointM

func NewPointM(srid int32, x, y, m float64) Geometry

NewPointM returns a point with a measure.

func NewPointZ

func NewPointZ(srid int32, x, y, z float64) Geometry

NewPointZ returns a point with an elevation.

func NewPointZM

func NewPointZM(srid int32, x, y, z, m float64) Geometry

NewPointZM returns a point with both an elevation and a measure.

func NewPolygon

func NewPolygon(srid int32, dim Dim, rings ...[]Coord) Geometry

NewPolygon returns a polygon from its rings, the first being the exterior one.

PostGIS decides whether the rings are closed and correctly nested; nothing here closes a ring for you, because a ring that needed closing is a ring somebody got wrong and silently fixing it hides that.

func (Geometry) AppendEWKB

func (g Geometry) AppendEWKB(dst []byte) []byte

AppendEWKB appends the geometry's EWKB encoding to dst and returns it.

The SRID is written whenever the geometry has one, which is what makes the round trip lossless: a geometry that went in as 4326 comes back as 4326 rather than as an unlabelled set of numbers.

func (Geometry) AsGeography

func (g Geometry) AsGeography() (Geography, error)

AsGeography reinterprets the geometry's coordinates as positions on the spheroid.

This is a cast, exactly as `geom::geography` is: the numbers do not change, only what they are taken to mean. It requires an SRID, because a geography with no spatial reference is a set of numbers with no surface to be on — and PostGIS's own cast would silently supply 4326, which is a guess this package will not make for you.

func (Geometry) Coords

func (g Geometry) Coords() []Coord

Coords returns every position of the geometry, in order.

The slice is freshly built, so a caller may keep or modify it without reaching back into the geometry — which matters because a geometry inside a built query has to stay the geometry that query was built from.

func (Geometry) Dim

func (g Geometry) Dim() Dim

Dim reports the geometry's dimensionality.

func (Geometry) EWKB

func (g Geometry) EWKB() []byte

EWKB returns the geometry's EWKB encoding.

func (Geometry) Equal

func (g Geometry) Equal(other Geometry) bool

Equal reports whether two geometries hold the same value.

This is structural equality — same shape, same dimensionality, same SRID, same coordinates in the same order — and it is deliberately not ST_Equals. PostGIS's ST_Equals is a topological question the server answers, and two geometries that trace the same shape with different vertex order are equal to it and not to this. Ask the server when the topological answer is what is meant.

func (Geometry) Geometries

func (g Geometry) Geometries() []Geometry

Geometries returns the members of a collection or multi geometry.

The result is a copy for the same reason Geometry.Coords is.

func (Geometry) IsEmpty

func (g Geometry) IsEmpty() bool

IsEmpty reports whether the geometry is one of PostGIS's empty values.

An empty geometry is not NULL. POINT EMPTY is a point that happens to have no position, and a column holding one holds a value; a NULL column holds none. The two are different in SQL and are different here.

func (Geometry) Kind

func (g Geometry) Kind() Kind

Kind reports the geometry's shape.

func (Geometry) NumPoints

func (g Geometry) NumPoints() int

NumPoints reports how many positions the geometry holds, across every ring and every member.

func (Geometry) SRID

func (g Geometry) SRID() int32

SRID reports the spatial reference the coordinates are in, which is UnknownSRID when the geometry has none.

func (Geometry) String

func (g Geometry) String() string

String renders the geometry for a person, in the shape of PostGIS's own EWKT.

It is a diagnostic rather than a serialisation: use ST_AsEWKT when the text has to be PostGIS's.

func (Geometry) WithSRID

func (g Geometry) WithSRID(srid int32) Geometry

WithSRID returns the geometry labelled with a spatial reference.

This is ST_SetSRID's semantics in Go: it changes which coordinate system the numbers are said to be in and does not touch the numbers. Use it when the coordinates are already right and only the label is missing.

type Kind

type Kind uint8

Kind is a geometry's shape.

const (
	KindPoint Kind = iota + 1
	KindLineString
	KindPolygon
	KindMultiPoint
	KindMultiLineString
	KindMultiPolygon
	KindCollection
)

The shapes this package models. They are PostGIS's own type codes in the same order, which is what makes the codec a lookup rather than a switch.

const AnyKind Kind = 0

AnyKind is the zero Kind: the column constrains no shape.

It is named so that generated code says what it means — a descriptor built with AnyKind reads better than one built with 0, and the two are the same value.

func (Kind) String

func (k Kind) String() string

String renders the shape as PostGIS spells it in a type modifier.

type NotInstalledError

type NotInstalledError struct{}

NotInstalledError reports a database with no PostGIS extension.

func (*NotInstalledError) Error

func (*NotInstalledError) Error() string

Error explains that the database has no PostGIS, and how to add it.

type NullGeogCol

type NullGeogCol[E any] struct {
	orm.NullCol[E, Geography]
	// contains filtered or unexported fields
}

NullGeogCol is a nullable geography column of entity E.

func NewNullGeogCol

func NewNullGeogCol[E any](src *orm.Source, name string, srid int32, kind Kind, dim Dim) NullGeogCol[E]

NewNullGeogCol returns a nullable geography column descriptor.

func (NullGeogCol[E]) Area

func (c NullGeogCol[E]) Area() orm.Value[E, *float64]

Area is the column's area in square metres, and can be NULL because the column can.

func (NullGeogCol[E]) Dim

func (c NullGeogCol[E]) Dim() Dim

Dim reports the dimensionality the column's type constrains it to.

func (NullGeogCol[E]) Expr

func (c NullGeogCol[E]) Expr() GeogExpr[E]

Expr lifts the column into a spatial expression on the spheroid, which is nullable because the column is.

func (NullGeogCol[E]) Kind

func (c NullGeogCol[E]) Kind() Kind

Kind reports the shape the column's type constrains it to, or zero when its type accepts any.

func (NullGeogCol[E]) Length

func (c NullGeogCol[E]) Length() orm.Value[E, *float64]

Length is the column's length in metres, and can be NULL because the column can.

func (NullGeogCol[E]) SRID

func (c NullGeogCol[E]) SRID() int32

SRID reports the coordinate system the column is declared in, or UnknownSRID when its type does not constrain one.

func (NullGeogCol[E]) TypeMod

func (c NullGeogCol[E]) TypeMod() TypeMod

TypeMod returns what the column's declared type says it holds, which is what FromExpr needs when that column is projected through a derived table.

type NullGeomCol

type NullGeomCol[E any] struct {
	orm.NullCol[E, Geometry]
	// contains filtered or unexported fields
}

NullGeomCol is a nullable geometry column of entity E.

It selects as *Geometry, because NULL and an empty geometry are different answers and only a pointer can tell them apart.

func NewNullGeomCol

func NewNullGeomCol[E any](src *orm.Source, name string, srid int32, kind Kind, dim Dim) NullGeomCol[E]

NewNullGeomCol returns a nullable geometry column descriptor.

func (NullGeomCol[E]) Area

func (c NullGeomCol[E]) Area() orm.Value[E, *float64]

Area is the column's area, and can be NULL because the column can.

func (NullGeomCol[E]) Dim

func (c NullGeomCol[E]) Dim() Dim

Dim reports the dimensionality the column's type constrains it to.

func (NullGeomCol[E]) Expr

func (c NullGeomCol[E]) Expr() GeomExpr[E]

Expr lifts the column into a spatial expression, which is nullable because the column is.

func (NullGeomCol[E]) Kind

func (c NullGeomCol[E]) Kind() Kind

Kind reports the shape the column's type constrains it to, or zero when its type accepts any.

func (NullGeomCol[E]) Length

func (c NullGeomCol[E]) Length() orm.Value[E, *float64]

Length is the column's length, and can be NULL because the column can.

func (NullGeomCol[E]) SRID

func (c NullGeomCol[E]) SRID() int32

SRID reports the coordinate system the column is declared in, or UnknownSRID when its type does not constrain one.

func (NullGeomCol[E]) TypeMod

func (c NullGeomCol[E]) TypeMod() TypeMod

TypeMod returns what the column's declared type says it holds, which is what FromExpr needs when that column is projected through a derived table.

type TypeMod

type TypeMod struct {
	// Family is whether the column is geometry or geography, which is the one
	// part of a modifier that is never optional: the two have different
	// operators and different answers for the same question.
	Family Family
	// Kind is the shape the column accepts, or zero for any.
	Kind Kind
	// Dim is the dimensionality the column accepts. It is [XY] both when the
	// column requires two dimensions and when it constrains nothing, which
	// HasDim separates.
	Dim Dim
	// SRID is the coordinate system the column requires, or [UnknownSRID] for
	// none.
	SRID int32
	// contains filtered or unexported fields
}

TypeMod is a spatial column's declared type, taken apart.

Zero values mean "the declaration does not constrain this", which is what a bare geometry says about all three. They do not mean a default: a column with no SRID constraint holds geometries whose SRIDs are their own business, and nothing here fills in 4326.

func ParseTypeMod

func ParseTypeMod(declared string) (TypeMod, error)

ParseTypeMod reads a PostgreSQL spatial type declaration.

It accepts what PostgreSQL renders and what a person writes, which differ only in case and spacing: geometry(Point,4326), GEOMETRY(POINTZ, 4326) and geometry are all the same declarations they look like. Anything that is not a spatial type parses to the zero TypeMod with no error, so a caller can ask about every column without deciding first.

It is strict about the parts it does understand. A shape PostGIS does not have, an SRID that is not a number, a modifier with three parts — each is an error naming what was wrong, because a declaration nobody can read is a column nobody can generate for.

func (TypeMod) Accepts

func (m TypeMod) Accepts(g Geometry) error

Accepts reports whether a value can be stored in a column of this declaration, and says why not when it cannot.

This is the check PostGIS performs on INSERT, done early enough to name the field. It is deliberately the same rule rather than a stricter one: a column with no modifier accepts anything, and a constrained one accepts exactly what its modifier says.

func (TypeMod) Constrained

func (m TypeMod) Constrained() bool

Constrained reports whether the declaration constrains the shape, dimensionality or coordinate system at all.

It is one answer rather than three because a PostGIS modifier constrains all three or none: geometry(Point,4326) requires two dimensions as much as it requires a point, and a bare geometry requires nothing.

func (TypeMod) GoType

func (m TypeMod) GoType() string

GoType names the Go type a column of this declaration reads into.

It is the generator's answer to "what does this field have to be", and it is two answers rather than seven: the shape lives in the value and in the descriptor's metadata, not in the Go type. A column typed geometry(Polygon,4326) reads into a Geometry that happens to be a polygon, because that is what PostGIS hands back and because ST_Intersection of two polygons is not always one.

func (TypeMod) Spatial

func (m TypeMod) Spatial() bool

Spatial reports whether this is a spatial type.

func (TypeMod) String

func (m TypeMod) String() string

String renders the type as PostgreSQL writes it, which is what makes a desired schema and an introspected one comparable as text.

Jump to

Keyboard shortcuts

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