Documentation
¶
Overview ¶
Package geometry provides 2D geometry primitives (Point, LineString, Polygon, MultiPoint) with WKB and WKT encoding, a coordinate reference system model, and common spatial operations (area, distance, centroid, convex hull, intersection tests, polygon boolean ops).
All coordinates are 2D. XY order (X = longitude/easting, Y = latitude/northing) matches the WKB, WKT, and GeoJSON specifications. Angles are always in degrees at the API surface; internal trig uses radians.
Index ¶
- Constants
- Variables
- func Area(g Geometry, u Unit) (float64, error)
- func BoundsCompatible(pred Predicate, ab, bb Bounds) bool
- func BoundsMinDistance(a, b Bounds) float64
- func CentroidAndBoundsFromWKB(data []byte) (Point, Bounds, error)
- func Contains(a, b Geometry) bool
- func ConvexHullFromXY(xs, ys []float64) (hullXs, hullYs []float64)
- func Crosses(a, b Geometry) bool
- func CrossesAntimeridian(g Geometry) bool
- func Euclidean(from, to Point, u Unit) (float64, error)
- func GeomDistance(a, b Geometry, u Unit) (float64, error)
- func Haversine(from, to Point, u Unit) (float64, error)
- func HaversineBatch(from, to []Point, u Unit) ([]float64, error)
- func HilbertIndex(x, y float64, bounds Bounds, order int) uint64
- func Intersects(a, b Geometry) bool
- func IsEmpty(g Geometry) bool
- func IsValid(g Geometry) bool
- func Length(g Geometry, u Unit) (float64, error)
- func MetersPerUnit(u Unit) (float64, error)
- func MultiPolygonRingViewsFromWKB(data []byte) ([][]PointsView, error)
- func Overlaps(a, b Geometry) bool
- func PIPFromWKB(data []byte, tx, ty float64) (bool, error)
- func PIPInclusiveFromWKB(data []byte, tx, ty float64) (bool, error)
- func PIPPolygonFromRings(rings []PointsView, tx, ty float64) bool
- func PIPRingFromXY(xs, ys []float64, tx, ty float64) bool
- func PROJJSONFor(epsg int32) map[string]any
- func PlanarAreaFromWKB(data []byte) (float64, error)
- func PlanarLengthFromWKB(data []byte) (float64, error)
- func PlanarMinDistanceFromWKB(a, b []byte) (float64, error)
- func PlanarRingArea(ring []Point) float64
- func PointToPolylineMinDistanceSq(px, py float64, xs, ys []float64, closed bool) float64
- func PointToSegmentDistanceSqXY(px, py, ax, ay, bx, by float64) float64
- func RegisterCRS(c CRS)
- func SimplifyDPFromXY(xs, ys []float64, tolerance float64) (outXs, outYs []float64)
- func String(g Geometry) string
- func Test(pred Predicate, a, b Geometry) bool
- func TestPointPrepared(pred Predicate, x, y float64, prep PreparedGeometry) bool
- func TestPointsPrepared(pred Predicate, xs, ys []float64, prep PreparedGeometry, out []bool)
- func TestPrepared(pred Predicate, a, b PreparedGeometry) bool
- func Touches(a, b Geometry) bool
- func TypeString(g Geometry) string
- func UTMEpsgFor(lon, lat float64) int32
- func UTMZoneFor(lon float64) int
- func WKB(g Geometry) []byte
- func WKBTypeCode(data []byte) (typ uint32, hasZ bool, err error)
- func Within(a, b Geometry) bool
- func WithinDistance(a, b Geometry, d float64) bool
- type BoolOp
- type Bounds
- type BufferOptions
- type BufferStyle
- type CRS
- type Circle
- type CircleFitMethod
- type CircleFitOptions
- type ClipOptions
- type Ellipse
- type Geometry
- func Boolean(a, b Geometry, op BoolOp, opts ClipOptions) (Geometry, error)
- func Buffer(g Geometry, distance float64, opts BufferOptions) (Geometry, error)
- func Clip(subject, mask Geometry) (Geometry, error)
- func Difference(a, b Geometry) (Geometry, error)
- func Dissolve(geoms []Geometry) (Geometry, error)
- func ParseWKB(data []byte) (Geometry, error)
- func ParseWKT(s string) (Geometry, error)
- func Project(g Geometry, target CRS) (Geometry, error)
- func Simplify(g Geometry, tolerance float64) (Geometry, error)
- func SplitAtAntimeridian(g Geometry) (Geometry, error)
- func SymDifference(a, b Geometry) (Geometry, error)
- func Union(a, b Geometry) (Geometry, error)
- type GeometryCollection
- func (c GeometryCollection) AppendWKB(buf []byte) []byte
- func (c GeometryCollection) Bounds() Bounds
- func (c GeometryCollection) CRS() CRS
- func (c GeometryCollection) Centroid() Point
- func (c GeometryCollection) EstimateUTMCRS() (CRS, error)
- func (c GeometryCollection) Is3D() bool
- func (c GeometryCollection) Type() Type
- func (c GeometryCollection) WKT() string
- type LineString
- func (l LineString) AppendWKB(buf []byte) []byte
- func (l LineString) Bounds() Bounds
- func (l LineString) Buffer(distance float64, segments int) Polygon
- func (l LineString) CRS() CRS
- func (l LineString) Centroid() Point
- func (l LineString) Clip(p Polygon) []LineString
- func (l LineString) EstimateUTMCRS() (CRS, error)
- func (l LineString) Is3D() bool
- func (l LineString) Length(u Unit) (float64, error)
- func (l LineString) Simplify(tolerance float64) LineString
- func (l LineString) SplitBy(p Polygon) (inside, outside []LineString)
- func (l LineString) ToCRS(target CRS) (LineString, error)
- func (l LineString) Type() Type
- func (l LineString) View() PointsView
- func (l LineString) WKT() string
- type MultiLineString
- func (m MultiLineString) AppendWKB(buf []byte) []byte
- func (m MultiLineString) Bounds() Bounds
- func (m MultiLineString) CRS() CRS
- func (m MultiLineString) Centroid() Point
- func (m MultiLineString) Clip(p Polygon) []LineString
- func (m MultiLineString) EstimateUTMCRS() (CRS, error)
- func (m MultiLineString) Is3D() bool
- func (m MultiLineString) Length(u Unit) (float64, error)
- func (m MultiLineString) LineViews() []PointsView
- func (m MultiLineString) SplitBy(p Polygon) (inside, outside []LineString)
- func (m MultiLineString) Type() Type
- func (m MultiLineString) WKT() string
- type MultiPoint
- func (m MultiPoint) AppendWKB(buf []byte) []byte
- func (m MultiPoint) Bounds() Bounds
- func (m MultiPoint) CRS() CRS
- func (m MultiPoint) Centroid() Point
- func (m MultiPoint) EstimateUTMCRS() (CRS, error)
- func (m MultiPoint) Is3D() bool
- func (m MultiPoint) Type() Type
- func (m MultiPoint) View() PointsView
- func (m MultiPoint) WKT() string
- type MultiPolygon
- func (m MultiPolygon) AppendWKB(buf []byte) []byte
- func (m MultiPolygon) Area(u Unit) (float64, error)
- func (m MultiPolygon) Bounds() Bounds
- func (m MultiPolygon) CRS() CRS
- func (m MultiPolygon) Centroid() Point
- func (m MultiPolygon) Contains(pt Point) bool
- func (m MultiPolygon) EstimateUTMCRS() (CRS, error)
- func (m MultiPolygon) Is3D() bool
- func (m MultiPolygon) PolygonRingViews() [][]PointsView
- func (m MultiPolygon) Type() Type
- func (m MultiPolygon) WKT() string
- type Point
- func AntimeridianCrossings(g Geometry) []Point
- func Centroid(g Geometry) Point
- func CentroidFromWKB(data []byte) (Point, error)
- func CircleIntersectionPoints(c1, c2 Circle) []Point
- func NewPoint(x, y float64, crs CRS) Point
- func NewPointZ(x, y, z float64, crs CRS) Point
- func SampleGeodesic(a, b Point, n int) ([]Point, error)
- func (p Point) AppendWKB(buf []byte) []byte
- func (p Point) Bounds() Bounds
- func (p Point) Buffer(distance float64, segments int) Polygon
- func (p Point) CRS() CRS
- func (p Point) Centroid() Point
- func (p Point) Distance(o Point, u Unit) (float64, error)
- func (p Point) Distance3D(o Point, u Unit) (float64, error)
- func (p Point) Equal(o Point) bool
- func (p Point) EstimateUTMCRS() (CRS, error)
- func (p Point) Is3D() bool
- func (p Point) ToCRS(target CRS) (Point, error)
- func (p Point) Type() Type
- func (p Point) WKT() string
- type PointsView
- type Polygon
- func (p Polygon) AppendWKB(buf []byte) []byte
- func (p Polygon) Area(u Unit) (float64, error)
- func (p Polygon) Bounds() Bounds
- func (p Polygon) Buffer(distance float64, segments int) Polygon
- func (p Polygon) CRS() CRS
- func (p Polygon) Centroid() Point
- func (p Polygon) Contains(pt Point) bool
- func (p Polygon) ConvexHull() Polygon
- func (p Polygon) EstimateUTMCRS() (CRS, error)
- func (p Polygon) Exterior() []Point
- func (p Polygon) Is3D() bool
- func (p Polygon) IsConvex() bool
- func (p Polygon) Perimeter(u Unit) (float64, error)
- func (p Polygon) RingViews() []PointsView
- func (p Polygon) Simplify(tolerance float64) Polygon
- func (p Polygon) ToCRS(target CRS) (Polygon, error)
- func (p Polygon) Type() Type
- func (p Polygon) WKT() string
- type Predicate
- type PreparedGeometry
- type RTree
- type Type
- type Unit
Constants ¶
const DefaultBufferSegments = 32
DefaultBufferSegments is the number of straight segments used to approximate a full circle in Buffer operations when the caller doesn't specify one.
const DefaultClipTolerance = 1e-10
DefaultClipTolerance is the relative tolerance used by the boolean-op engine when the caller doesn't set one. 1e-10 gives ~13 significant figures — enough for coastal-scale UTM coordinates (~1e6 m).
const DefaultHilbertOrder = 16
DefaultHilbertOrder is the resolution used when SortByHilbert or downstream helpers omit an explicit order. 16 bits per axis = 65,536 × 65,536 cells, which gives sub-parts-per-million spatial discrimination on any planet-scale bounding box (~30 meters on a WGS84 world-extent bbox) — enough that within-row-group locality dominates any residual quantization error.
const EarthRadiusKM = 6371.0088
EarthRadiusKM is the mean Earth radius used by haversine calculations.
const RTreeNodeSize = 16
RTreeNodeSize is the maximum number of children in an R-tree node. Values around 16 balance memory density and traversal cost.
Variables ¶
var ( WGS84 = CRS{EPSG: 4326, Name: "WGS 84", Projected: false} PseudoMercator = CRS{EPSG: 3857, Name: "WGS 84 / Pseudo-Mercator", Projected: true} )
Known CRSes. The set is intentionally small; add as needed.
var ( ErrShortWKB = errors.New("geometry: WKB too short") ErrInvalidByteOrder = errors.New("geometry: invalid WKB byte order") ErrUnsupportedWKB = errors.New("geometry: unsupported WKB geometry type") ErrTypeMismatch = errors.New("geometry: WKB type does not match target") ErrEmptyGeometry = errors.New("geometry: geometry has no points") ErrInvalidWKT = errors.New("geometry: invalid WKT") ErrUnknownCRS = errors.New("geometry: unknown CRS") ErrInvalidUnit = errors.New("geometry: invalid distance unit") ErrCRSMismatch = errors.New("geometry: operation requires geometries in the same CRS") ErrProjectionMissing = errors.New("geometry: reprojection between these CRSes is not implemented") )
var ErrAntimeridianCrossing = errors.New("geometry: geometry crosses the antimeridian")
ErrAntimeridianCrossing indicates that an operation received a geographic-CRS geometry whose vertices span the ±180° meridian and cannot be interpreted correctly under gobi's cartesian primitives without pre-splitting. Callers should either
- route through SplitAtAntimeridian, or
- reproject to a projected CRS that avoids the discontinuity.
var ErrAntipodalPoints = errors.New("geometry: cannot interpolate a unique great circle through antipodal points")
ErrAntipodalPoints is returned when SampleGeodesic is asked to interpolate between two points that are exact antipodes: the great circle through them isn't unique, so there's no canonical arc to sample. Callers can dodge this by nudging one endpoint slightly or splitting the path through an intermediate waypoint.
var ErrCircleFit = errors.New("geometry: cannot fit circle to input")
ErrCircleFit is returned by FitCircle when the input cannot resolve to a unique circle (e.g. fewer than 3 points, or points collinear within numerical tolerance).
var ErrEllipseFromFoci = errors.New("geometry: cannot construct ellipse from foci")
ErrEllipseFromFoci is returned by EllipseFromFoci when the given major-axis length is too small to accommodate the two foci (majorAxis < |f1 - f2|), which is geometrically impossible.
var ErrGeodesicRequiresGeographic = errors.New(
"geometry: geodesic ops require a geographic CRS (X = longitude, Y = latitude in degrees)")
ErrGeodesicRequiresGeographic is returned by SampleGeodesic / DensifyGeodesic when the input isn't in a geographic CRS. "Great-circle" only makes sense on lon/lat coordinates; projected- CRS callers wanting "insert intermediate vertices in a straight line" should linearly interpolate themselves.
var ErrGeographicCRS = errors.New("geometry: boolean ops require a projected CRS")
ErrGeographicCRS is returned by boolean-op functions when either input is in a geographic (non-projected) CRS. The current engine is planar-only; geographic inputs must be reprojected to a projected CRS first (see EstimateUTMCRS / ToCRS).
Functions ¶
func BoundsCompatible ¶ added in v0.4.1
BoundsCompatible reports whether the two bounds are compatible under pred's necessary-condition. Wrong answers here would falsely reject valid predicate matches, so err on the side of "compatible" for any predicate we don't specifically handle.
Used by TestPrepared internally and by the per-row bbox-reject fast paths in Series predicate ops (which read row bboxes via BoundsFromWKB and can short-circuit false without a full ParseWKB when this returns false).
PredIntersects / PredTouches / PredCrosses / PredOverlaps: bboxes must overlap. PredContains: a's bbox must cover b's bbox. PredWithin: b's bbox must cover a's bbox.
func BoundsMinDistance ¶ added in v0.4.1
BoundsMinDistance returns the minimum Euclidean distance between two axis-aligned bounding rectangles. Zero when they overlap or touch. Empty bounds → +Inf.
Used by WithinDistance's short-circuit; also exposed publicly so per-row `Series.GeomDWithin` callers can reject far rows via `BoundsFromWKB` + this helper without a full ParseWKB.
func CentroidAndBoundsFromWKB ¶ added in v0.4.1
CentroidAndBoundsFromWKB is the fused-scan variant: computes the centroid AND the 2D bounding box in a single byte-stream pass. The centroid semantics match CentroidFromWKB; the bounds semantics match BoundsFromWKB. Callers who need both (e.g. the fused HilbertSortWithCovering write path) save a full second byte-scan.
func ConvexHullFromXY ¶ added in v0.4.1
ConvexHullFromXY runs Andrew's monotone-chain algorithm on parallel Xs / Ys slabs and returns the convex hull as a fresh pair of slabs in counter-clockwise order, with the closing vertex (a repeat of the first) appended so the output is a closed ring.
Design vs the AoS Graham scan ¶
The AoS Polygon.ConvexHull in polygon.go runs Graham scan: find a pivot, sort every other vertex by polar angle from the pivot via `sort.Slice` on []Point (which boxes the closure function-value and moves 40-byte Point structs during partition), then stack-scan retaining CCW turns. Two nested allocations (the closure box + the sorted []Point copy) plus point-struct swaps in-place.
Andrew's monotone chain replaces the polar-angle sort with an index sort by (x, y) lex — the resulting order is the same linear structure for the two half-hulls. Two O(n) stack scans over an index permutation (lower hull, then upper hull) produce the CCW output. Sort is on `[]int` indices via `slices.SortFunc` (8-byte swaps + typed callback — no closure boxing that `sort.Slice` would incur); the hull-scan reads coordinates directly from the input slabs so the inner-loop arithmetic operates on cache-friendly float64 arrays.
Semantics ¶
- Fewer than 3 unique points return a copy of the input slabs (no closing vertex appended — matches the AoS shape which also returns the input unchanged when Exterior() has <3 points).
- Duplicate points are tolerated. Since the (x,y) lex order is a total order over distinct points, sort stability is irrelevant — equal keys mean the same point, and the CCW scan drops the extra via strict `<= 0` cross-product rejection just like collinear points on the hull edge (matches the AoS behavior).
- Output vertex count includes the closing repeat, so a hull with k unique vertices has k+1 entries.
func Crosses ¶ added in v0.3.0
Crosses reports whether a and b have some interior points in common but not all — typically LineString vs Polygon or LineString vs LineString. Matches shapely's a.crosses(b).
func CrossesAntimeridian ¶ added in v0.3.0
CrossesAntimeridian reports whether any adjacent vertex pair in g has |Δlon| > 180°, which under WGS84 interpretation means the edge between them wraps around the ±180° meridian. Returns false for projected-CRS inputs (the concept doesn't apply) and for empty geometries. Assumes X = longitude, Y = latitude in degrees, matching WKB convention.
func Euclidean ¶
Euclidean returns the planar distance between two Points, in the requested unit. The input coordinates are assumed to already be in meters (projected CRS); Z / CRS are ignored.
Breaking change in v0.2.16: previously took four float64 args (x1, y1, x2, y2). The new signature aligns with Haversine + HaversineBatch + Point.Distance. Migration: replace `Euclidean(a.X, a.Y, b.X, b.Y, u)` with `Euclidean(a, b, u)`.
func GeomDistance ¶ added in v0.3.0
GeomDistance returns the minimum planar (Euclidean) distance between any two points in a and b. Returns 0 when they intersect. Uses point-to-segment distance across all vertex-vs-edge pairs from both sides — O(V_a·E_b + V_b·E_a) in the general case.
Coordinates are treated as planar meters; for geographic (WGS84 lon/lat) inputs the result is Euclidean on degrees, which is meaningless. Project to a suitable CRS first (see Point.Distance / Haversine for lon/lat point pairs).
func Haversine ¶
Haversine returns the great-circle distance between two lon/lat Points on a sphere of Earth radius, in the requested unit. Point X = longitude, Y = latitude (WKB convention); Z / CRS are ignored.
Breaking change in v0.2.16: previously took four float64 args (lon1, lat1, lon2, lat2). The new signature aligns with HaversineBatch and Point.Distance so callers holding geometry types can pass them directly. Migration: replace `Haversine(a.X, a.Y, b.X, b.Y, u)` with `Haversine(a, b, u)`.
func HaversineBatch ¶ added in v0.2.16
HaversineBatch returns per-pair great-circle distances between from[i] and to[i] in the requested unit. Semantically equivalent to calling Haversine(from[i], to[i], u) in a loop, but bulk-optimized:
- The unit conversion factor, Earth-radius constant, and degree-to-radian scale are hoisted outside the inner loop (one metersPerUnit call for the whole batch).
- Per-row math runs in a fixed-count vars body — Go's inliner keeps it tight vs. the per-call scalar Haversine which pays a function-call boundary + defer + err-check per row.
Both input slices must be the same length; a mismatch returns ErrColumnLenMismatch. Empty slices are legal and return an empty (non-nil) result.
CRS on each Point is ignored — Haversine is a lon/lat sphere computation that expects Y=latitude, X=longitude in degrees. Points in a projected CRS give nonsensical distances; converting via ToCRS(WGS84) before calling is the caller's responsibility.
Return type is a flat []float64 — same shape a downstream SIMD kernel or arrow builder wants. Nulls in the input aren't signaled (Point isn't a nullable type); to skip-and-preserve row positions, either pre-filter or pass sentinel points and mask the output.
func HilbertIndex ¶ added in v0.3.5
HilbertIndex returns the 1D position of (x, y) along a space-filling Hilbert curve of the given order, computed after normalizing (x, y) into a `2^order × 2^order` integer grid derived from bounds.
The Hilbert curve preserves 2D locality: points close in (x, y) tend to be close in HilbertIndex. Sorting rows by their centroid's HilbertIndex before writing a GeoParquet file makes per-row-group bboxes small, which is what the v0.3.4 row-group pushdown machinery actually needs to prune usefully on real data.
Contract:
- bounds must be a valid non-empty rectangle. Empty bounds return 0 (all points "index to origin"), which sorts to random-adjacent order — a no-op rather than a crash.
- order in [1, 31]. Values outside clamp to DefaultHilbertOrder. Order 16 (default) is enough for continental datasets; order 24+ approaches meter-scale on a WGS84 world extent.
- Points outside bounds are clamped to the boundary before quantization — an off-by-a-hair polygon still gets a stable index.
The 2D Hilbert construction is the standard iterative quadrant-rotation algorithm (see e.g. Wikipedia's "Hilbert curve" article, xy2d). No lookup tables — hot loop is O(order) integer ops, ~30 ns/call at order=16.
func Intersects ¶
Intersects reports whether a and b share any point. The relation is symmetric.
func IsEmpty ¶ added in v0.3.0
IsEmpty reports whether g has no coordinates. Matches shapely's .is_empty semantics: a Point with the default (0,0) is NOT empty (shapely's convention), but a Polygon with no rings, a LineString with fewer than 2 points, or a MultiPolygon with no components IS.
func IsValid ¶ added in v0.3.0
IsValid reports whether g satisfies the OGC Simple Features validity rules gobi checks:
- LineString: >= 2 points, no consecutive duplicate vertices.
- Polygon: every ring closed (or auto-closable), >= 3 unique vertices per ring, no ring self-intersection.
- Multi types: all components valid.
- Point / MultiPoint: always valid.
This is deliberately a subset of GEOS's IsValid — full OGC validity includes checks like "holes lie inside exterior" and "rings don't touch except at points" which require more machinery than gobi currently exposes. Returns false for structurally-broken input; callers can rely on IsValid == true meaning "safe to pass to Boolean / Buffer without triggering degenerate-input paths."
func Length ¶
Length returns the planar (XY) length of g in u. Non-linear geometries (Point, MultiPoint, Polygon) return 0. Polygons don't return perimeter here — use Polygon.Perimeter for that.
func MetersPerUnit ¶ added in v0.2.16
MetersPerUnit returns the number of meters in one of the given unit. Exported so callers building their own bulk distance kernels can hoist the scale factor outside a hot loop instead of paying a per-call `metersPerUnit` lookup.
func MultiPolygonRingViewsFromWKB ¶ added in v0.4.1
func MultiPolygonRingViewsFromWKB(data []byte) ([][]PointsView, error)
MultiPolygonRingViewsFromWKB parses a MultiPolygon or MultiPolygonZ WKB directly into a [][]PointsView with one []PointsView per sub-polygon. Same layout as MultiPolygon.PolygonRingViews() but no []Point intermediate.
func Overlaps ¶ added in v0.3.0
Overlaps reports whether a and b share interior points but neither contains the other, and both are of the same dimension (both areas or both lines). Matches shapely's a.overlaps(b).
func PIPFromWKB ¶ added in v0.4.1
PIPFromWKB reports whether (tx, ty) lies inside the polygon encoded in data. Semantics match Polygon.Contains(pt) for Polygon inputs (test point against exterior, exclude holes) and MultiPolygon "point in any constituent polygon" for MultiPolygon inputs. Non-polygon type codes (Point, LineString, etc.) return (false, nil) — matches the AoS shape where Polygon.Contains isn't defined on those types.
This is Slice 4's WKB-facing entry point. Walks the byte stream once with an inline even-odd crossing test — no []Point or Polygon allocation, no `closedRing` copy for unclosed rings. Zero allocation per call.
Same "points on the boundary have undefined containment" caveat as the AoS pointInRing kernel. Callers requiring boundary-inclusive semantics should pair this with a separate on-boundary scan, matching the pointInPolygon → pointOnPolygonBoundary shape in the AoS path.
func PIPInclusiveFromWKB ¶ added in v0.4.1
PIPInclusiveFromWKB reports whether (tx, ty) lies inside the polygon encoded in data OR on its boundary. Semantics match the AoS `pointInPolygon(pt, poly)` exactly, i.e. boundary-inclusive containment. This is the entry point SJoin's Points × Polygons refine needs — the strict-interior `PIPFromWKB` has documented undefined behavior on boundaries, which would silently change SJoin semantics for grid-aligned data.
Single WKB pass: extends the ring scan to also do a point-on-segment test per segment (collinearity + within-bbox). Returns true on first boundary hit; otherwise finalizes with the crossing-count parity. Zero allocation on well-formed input.
Non-polygon type codes (Point, LineString, etc.) return (false, nil) — matches PIPFromWKB.
func PIPPolygonFromRings ¶ added in v0.4.1
func PIPPolygonFromRings(rings []PointsView, tx, ty float64) bool
PIPPolygonFromRings tests point-in-polygon over a full polygon represented as an ordered slice of PointsViews: rings[0] is the exterior ring; rings[1:] are holes. Returns true iff (tx, ty) lies inside the exterior and outside every hole.
Semantics match Polygon.Contains on the AoS representation. Zero allocation. Callers holding a Polygon can obtain the RingViews once via `polygon.RingViews()` (Slice 1 amortization) and reuse the slice across many candidate points — the common pattern in spatial-join refine loops.
Empty rings input returns false.
func PIPRingFromXY ¶ added in v0.4.1
PIPRingFromXY reports whether the point (tx, ty) lies inside the polygon ring defined by parallel Xs / Ys slices. Even-odd crossing rule; matches the semantics of pointInRing on the AoS `[]Point` representation.
Handles both closed rings (last point equals first) and unclosed rings — the crossing test walks segments (i, i+1) for i in [0, n-1) and (if the ring isn't already closed) implicitly the (n-1, 0) closing segment via the modulo-wrap loop shape.
Points on the boundary have undefined containment, same as the AoS pointInRing kernel. Callers requiring boundary-inclusive semantics should combine with a separate on-boundary check (matches pointInPolygon → pointOnPolygonBoundary in the AoS path).
Empty ring (fewer than 3 points) or mismatched-length slices return false. Zero-allocation on every input.
func PROJJSONFor ¶ added in v0.3.0
PROJJSONFor returns the canonical PROJJSON object for the given EPSG code, or nil if we don't have one on file. Callers include this in GeoParquet's "geo" metadata so downstream readers (geopandas via pyproj) can round-trip the CRS.
func PlanarAreaFromWKB ¶ added in v0.4.1
PlanarAreaFromWKB returns the absolute planar (XY) area of the WKB geometry in coord² without materializing intermediate []Point / Polygon structs. Polygon area is exterior − holes; MultiPolygon sums per-polygon area; GeometryCollection recurses. Non-areal types (Point, LineString, MultiPoint, MultiLineString) contribute 0.
Semantics match `PlanarRingArea` composed via `Polygon.Area` on a projected CRS — i.e. the shoelace formula applied ring-by- ring with unclosed rings treated as if the first vertex were virtually appended. Unit conversion is left to the caller (multiply by `1 / (perM*perM)` for a projected CRS whose linear unit is meters). Geographic CRSes must fall back to the AoS spherical-excess path.
Byte-order / type-code handling mirrors ParseWKB; malformed input returns an error. Zero-allocation on well-formed input.
func PlanarLengthFromWKB ¶ added in v0.4.1
PlanarLengthFromWKB returns the planar (XY) length of the WKB geometry in coordinate units without materializing any intermediate []Point / LineString structs. Sums Euclidean segment lengths for LineString / MultiLineString; recurses into GeometryCollections. All other type codes (Point, MultiPoint, Polygon, MultiPolygon) contribute 0 — matches the AoS shape of `geometry.Length` where non-linear geometries return 0.
This is the SoA sibling of BoundsFromWKB / CentroidFromWKB for callers that only need the planar linear extent — e.g. a Series GeomLength column over a projected CRS. Returns coordinate-unit length; Unit conversion is left to the caller (multiply by `1 / metersPerUnit(u)` for a projected CRS whose linear unit is meters). Geographic CRSes must fall back to the AoS Haversine path — the WKB blob doesn't carry CRS context.
Byte-order / type-code handling mirrors ParseWKB; malformed input returns an error. Zero-allocation on well-formed input.
func PlanarMinDistanceFromWKB ¶ added in v0.4.1
PlanarMinDistanceFromWKB returns the min planar Euclidean distance between two WKB-encoded geometries. Parses both to slab-form representations directly (no `[]Point` intermediate) and runs the Slice-11 min-distance nested loop with a single `math.Sqrt` at the end.
Allocation shape ¶
The parse skips `[]Point` materialization but does `append` onto per-role slabs (pointXs/pointYs, polylines). For most workloads this amortizes to a small constant number of `growslice` calls; callers that need strict zero-alloc must pre-scan geometry counts and pass hinted slabs — not a supported entry point today.
Non-intersection assumption ¶
This function is a fast path for the (bbox-disjoint → definitely non-intersecting) case that `Series.GeomDistance` uses when filtering rows against a fixed `other`. It does NOT run a segment-segment intersects check; intersecting geometries can produce a nonzero distance (matching the vertex-to-segment approach that `planarMinDistance` returns for non-intersecting inputs). Callers that need `Intersects → 0` semantics must verify bboxes are disjoint or fall through to the AoS `GeomDistance(a, b, u)` path.
Returns math.Inf(+1) when both inputs are empty (no vertex or segment to compare against). Malformed WKB returns an error.
func PlanarRingArea ¶ added in v0.3.4
PlanarRingArea returns the planar (unsigned) area of a ring via the shoelace formula. Independent of CRS — callers that need the spherical-area interpretation for geographic coordinates should use Polygon.Area with a Unit instead. Exposed so downstream packages (predicate stats, buffer heuristics) can compute planar areas without re-implementing the shoelace loop.
Rings that aren't explicitly closed (first vertex != last vertex) are treated as if the caller had appended the first vertex again.
func PointToPolylineMinDistanceSq ¶ added in v0.4.1
PointToPolylineMinDistanceSq returns the minimum squared Euclidean distance from (px, py) to any point on the polyline held in parallel Xs / Ys slabs. When closed=true the closing segment (last, first) is also considered — matching the ring closure that Polygon.Segments enumerates.
Empty polyline returns math.Inf(1) (no segments to compare against). Single-point polyline returns squared distance from (px, py) to that single vertex.
Zero-alloc; single pass over the polyline slabs.
func PointToSegmentDistanceSqXY ¶ added in v0.4.1
PointToSegmentDistanceSqXY returns the squared Euclidean distance from (px, py) to the closed line segment ((ax, ay), (bx, by)). Handles a == b (zero-length segment) as squared point-to-point distance.
Why squared ¶
Min-distance loops repeatedly compare distances and only need the sqrt on the final answer. The classic AoS pointToSegmentDistance in distance_geom.go calls math.Hypot per segment; on a polygon×polygon distance with ~100 vertices each, that's 10k sqrts per per-row call. Squared form defers to a single sqrt at the outermost call — see planarMinDistance's slab-form rewrite (Slice 11).
The formula is the standard projection-onto-line-segment:
t = ((p-a) · (b-a)) / |b-a|² clamped to [0, 1] f = a + t·(b-a) nearest point on segment d² = (p.x - f.x)² + (p.y - f.y)²
When |b-a|² == 0 the segment is a single point; return squared distance from p to a directly.
func RegisterCRS ¶
func RegisterCRS(c CRS)
RegisterCRS adds a CRS to the runtime registry. Overwrites any prior entry for the same EPSG code.
func SimplifyDPFromXY ¶ added in v0.4.1
SimplifyDPFromXY runs iterative Douglas-Peucker on parallel Xs / Ys slabs, returning a fresh pair of slabs containing the retained coordinates. Endpoints are always preserved. tolerance ≤ 0 or n < 3 returns a copy of the input coordinates.
Design vs. the AoS douglasPeucker ¶
The AoS `douglasPeucker([]Point, float64)` in simplify.go is recursive; at every split it slices the []Point twice and stitches the two halves with a fresh append allocation. Frequent splits on real-world polylines (coastlines, admin boundaries) mean O(log n) heap allocations per polyline alongside the O(n) `[]Point` walks the recursion drives.
This SoA rewrite is iterative on an explicit (lo, hi) stack plus a keep-bitmap:
- One []bool allocation of length n.
- One stack allocation (typically log₂(n) frames — 20-ish for a coastline-scale ring).
- Two final output []float64 allocations sized to the exact retained count.
Total: 3-4 allocations regardless of split count, vs the AoS recursion's O(log n) per-split appends.
The perpendicular-distance kernel avoids the sqrt+div on non-splitting segments: instead of computing `d = |cross| / segLen` per point and comparing against `tolerance`, it tracks `argmax(cross²)` across the sub-array and compares once against `tolerance² * segLen²`. Saves one sqrt per split when no interior point exceeds tolerance (the common case near the leaves of the DP tree).
Determinism vs. AoS ¶
Split order matches the AoS recursion (left before right) so the produced vertex indices are identical for well-formed input. Tie-breaking on argmax also matches (both use strict `>` which picks the first occurrence of the max).
func TestPointPrepared ¶ added in v0.4.2
func TestPointPrepared(pred Predicate, x, y float64, prep PreparedGeometry) bool
TestPointPrepared evaluates pred on (Point{x, y}, prep.G) without the interface-boxing allocation that Prepare(Point{...}) + TestPrepared would incur. The atomic query underlying TestPointsPrepared — the batch API is essentially a loop over this with a held R-tree scratch.
Fast-path shapes (same as TestPrepared, non-swapped ordering):
- Point × Polygon: pred ∈ {Intersects, Within}
- Point × MultiPolygon: pred ∈ {Intersects, Within}
Every other pred / prep shape falls through to per-call Test — no panic, no error, just AoS semantics.
Concurrent-safe against the prep (atomic lazy slots).
func TestPointsPrepared ¶ added in v0.4.2
func TestPointsPrepared(pred Predicate, xs, ys []float64, prep PreparedGeometry, out []bool)
TestPointsPrepared evaluates pred for each (Point{xs[i], ys[i]}, prep.G) pair, writing results into out. Semantics match a per- index Test(pred, Point{X: xs[i], Y: ys[i]}, prep.G).
The batch amortizes prep-side work across every point:
- Ring views / R-tree / bounds are materialized once by Prepare and shared across the entire call.
- No per-point interface-boxing of the point into a Geometry (single-point TestPrepared costs one alloc per Prepare(Point{...})).
- MultiPolygon R-tree Search scratch is held for the whole batch, not Get/Put'd per point.
- Bbox reject is inlined into the loop with no dispatch overhead.
Fast-path shapes (same as TestPrepared, non-swapped ordering):
- Point × Polygon: pred ∈ {Intersects, Within}
- Point × MultiPolygon: pred ∈ {Intersects, Within}
Every other pred (Contains, Disjoint, ...) or prep shape falls through to per-point Test — correctness preserved, no batch speedup.
len(xs) must equal len(ys); len(out) must be ≥ len(xs). Panics on mismatch (matches compute-package convention).
Concurrent-safe against the prep — atomic lazy slots let multiple goroutines batch-query the same prep. The out buffer obviously must not be shared across goroutines.
func TestPrepared ¶ added in v0.4.1
func TestPrepared(pred Predicate, a, b PreparedGeometry) bool
TestPrepared evaluates pred on the ordered pair (a, b) using precomputed SoA views when the pair shape has a fast path. Semantically equivalent to Test(pred, a.G, b.G).
Nil geometry on either side returns false for every predicate (matches Test). Non-fast-path shapes fall through to Test — the only cost of using TestPrepared over Test on those shapes is the bounds-reject double-check + the type-switch overhead (single-digit nanoseconds per call).
func Touches ¶ added in v0.3.0
Touches reports whether a and b share at least one boundary point but no interior points. Matches shapely's a.touches(b). Empty geometries return false.
func TypeString ¶ added in v0.3.0
TypeString returns the OGC-style name for g's concrete type ("Point", "MultiPolygon", etc.). Matches shapely's .geom_type output.
func UTMEpsgFor ¶
UTMEpsgFor returns the EPSG code of the UTM CRS covering (lon, lat) on the WGS84 datum. Latitude sign selects hemisphere.
func UTMZoneFor ¶
UTMZoneFor returns the UTM zone number [1..60] whose central meridian nearest longitude lon.
func WKBTypeCode ¶ added in v0.4.1
WKBTypeCode returns the OGC type code (and whether Z is present) of the WKB blob without decoding any coordinates. Zero-alloc. Callers dispatching per-type fast paths (SoA scanners vs AoS fallback based on shape) use this to peek before committing to a full ParseWKB.
Returned codes match the WKB spec: 1=Point, 2=LineString, 3=Polygon, 4=MultiPoint, 5=MultiLineString, 6=MultiPolygon, 7=GeometryCollection. hasZ=true when the code is the 1001-1007 variant.
func WithinDistance ¶ added in v0.3.5
WithinDistance reports whether any two points in a and b are at most d coordinate units apart. Equivalent to `GeomDistance(a, b) <= d` but with a bbox-distance short-circuit that lets far-apart pairs return false without walking edges.
d must be non-negative; d = 0 is equivalent to Intersects(a, b). Nil operands or NaN d return false. Coordinates are treated as planar — for lon/lat inputs, project to a suitable CRS first (Haversine + a per-row loop covers the geographic case).
The bbox short-circuit computes the minimum distance between the two bounding rectangles: if that's already > d, no interior point pair could be closer. This is what makes DWithin's row- group pushdown pay off — a row whose bbox is far from an AOI bbox never gets its WKB decoded.
Types ¶
type BoolOp ¶ added in v0.3.0
type BoolOp uint8
BoolOp names a binary boolean operation on polygons.
const ( // OpIntersection returns the area contained by both operands. OpIntersection BoolOp = iota // OpUnion returns the area contained by either operand. OpUnion // OpDifference returns subject minus clipping (subject \ clipping). OpDifference // OpSymDifference returns the area in exactly one of the operands. OpSymDifference )
type Bounds ¶
type Bounds struct {
MinX, MinY, MaxX, MaxY float64
}
Bounds is an axis-aligned 2D bounding box: (MinX, MinY, MaxX, MaxY).
func BoundsFromWKB ¶ added in v0.4.1
BoundsFromWKB computes the axis-aligned 2D bounding box of a WKB geometry without materializing any intermediate Point / Polygon / etc. structs. Walks the byte stream once, tracking running min/max on X and Y.
This is Slice 2's SoA fast path for bbox-only callers — the parquetio bbox-covering-column write path, GeoParquet metadata bounds compute, and any Filter/predicate hot path that only needs the bbox of each input geometry. Skips the O(n) `[]Point` allocation that ParseWKB does even though the caller throws the geometry away immediately after `.Bounds()`.
Semantics match `ParseWKB(data).Bounds()` exactly:
- Empty geometries (empty LineString / Polygon / GeometryCollection) return EmptyBounds().
- Z coordinates are ignored — matches the 2D Bounds type.
- MultiPoint / MultiLineString / MultiPolygon / GeometryCollection recursively include every sub-geometry's coordinates. Nested GeometryCollections are rejected inside a GeometryCollection (matching ParseWKB).
- Byte-order and type-code handling mirrors ParseWKB; unsupported type codes return ErrUnsupportedWKB.
The scanner is per-call zero-allocation on well-formed input. Malformed input returns an error without leaking partial state.
func BoundsFromXY ¶ added in v0.4.1
BoundsFromXY computes the axis-aligned bounding box of the points held in parallel Xs / Ys slices. Assumes len(xs) == len(ys); a mismatch is caller error and produces bounds derived from the shorter slice.
This is the SoA-form kernel that spatial-predicate hot paths call once they've materialized (or been handed) a PointsView. Kept as a free function so callers holding raw `[]float64` slabs — e.g. from a WKB parse-into-slab entry point in a future slice, or from arrow-backed coordinate columns — can call it directly without constructing a PointsView.
Portable Go body — no SIMD wire-in. Slice 6a shipped compute.BoundsF64 with a SIMD variant that wins 2.4× on Apple silicon (deep-OOO cores) but LOSES 48% on Ampere Neoverse (throughput-tuned server cores where vector Min/Max costs more per lane than scalar compare-branch). Rather than pick a default that regresses on one class of production hardware, keep the scalar body here and let advanced callers who know their target arch call compute.BoundsF64 explicitly. See the Slice 6 recap in .vscode/CLAUDE.md for the full Apple-vs-Ampere measurement.
func EmptyBounds ¶
func EmptyBounds() Bounds
EmptyBounds returns a bounds value that will always be extended by the first point passed to Extend.
func (Bounds) Contains ¶
Contains reports whether the bounds contain (x, y). The upper edges are inclusive.
func (Bounds) Empty ¶
Empty reports whether the bounds are the inverted-sentinel returned by EmptyBounds — i.e. no point has ever been added to them.
Empty is NOT the same as "zero-area rectangle." A Point's Bounds is {x, y, x, y} (min == max on both axes), which is not Empty — it's a legitimate zero-area rectangle carrying the point's location. Only the inverted sentinel signals "unset." Use IsZero to test for the zero-value Bounds{} shape, which callers using bounds as a reference-frame parameter often want to treat as "derive from data."
func (Bounds) Intersects ¶
Intersects reports whether the two bounds overlap.
func (Bounds) IsZero ¶ added in v0.3.5
IsZero reports whether the bounds are the Go zero-value Bounds{} (all four fields == 0). Distinct from Empty (which catches the inverted-sentinel EmptyBounds()). Callers that accept bounds as an optional reference frame (see HilbertSortOptions.Bounds) treat IsZero as "unspecified — derive from data" so users don't need to know about the sentinel form.
type BufferOptions ¶
type BufferOptions struct {
// Segments controls the number of straight edges used to approximate a
// full circle in BufferRound style. Higher = smoother, larger output.
// 0 falls back to DefaultBufferSegments. Ignored when Style is
// BufferSquare.
Segments int
// Style picks between rounded and square outputs. See BufferStyle.
Style BufferStyle
}
BufferOptions configures Buffer behavior.
type BufferStyle ¶ added in v0.3.0
type BufferStyle uint8
BufferStyle picks the shape of Buffer's caps and joins.
const ( // BufferRound emits rounded caps (semicircles) and rounded convex // joins. Matches shapely's cap_style=round + join_style=round. // Default (zero value). BufferRound BufferStyle = iota // BufferSquare emits square outputs: Point becomes an // axis-aligned square of half-width = distance; LineString gets // flat caps extended by distance and mitre joins; Polygon gets // mitre (sharp) joins. Matches shapely's cap_style=square + // join_style=mitre. Faster than BufferRound and produces fewer // vertices, but strongly-concave inputs may need clamping. BufferSquare )
type CRS ¶
CRS identifies a coordinate reference system. Only EPSG code is authoritative for equality — Name is a human-readable label.
type Circle ¶ added in v0.3.1
Circle is a first-class circular shape defined by its center and radius. Deliberately NOT a Geometry: OGC SFA / WKB has no encoding for circles, so callers who need to serialize should lower via Boundary or BoundaryLine first.
Radius units follow Center.CRSValue: meters for UTM / PseudoMercator, degrees for WGS84. Callers wanting a Circle "in meters" from a geographic-CRS point cluster should reproject the input via ToCRS before calling FitCircle.
func FitCircle ¶ added in v0.3.1
func FitCircle(points []Point, opts CircleFitOptions) (Circle, []float64, error)
FitCircle fits a Circle to the input points via least squares and returns per-point signed geometric residuals — residuals[i] is (|points[i] - center| - radius), positive when the point is outside the fit circle, negative when inside. Requires at least 3 points. Returns ErrCircleFit when the points are collinear or the system is otherwise degenerate.
The fit is 2D in the coordinate plane of the input Points; Z is ignored. Output Circle's CRS is inherited from the first point.
func (Circle) Boundary ¶ added in v0.3.1
Boundary returns a closed Polygon approximating the circle with nVertices segments (nVertices < 4 falls back to DefaultBufferSegments for consistency with Point.Buffer). The resulting polygon has exactly nVertices+1 points (the last equals the first) and is wound CCW.
func (Circle) BoundaryLine ¶ added in v0.3.1
func (c Circle) BoundaryLine(nVertices int) LineString
BoundaryLine returns an open LineString along the circle's circumference (n vertices, NOT closed — the last point is not a repeat of the first). Useful when callers want to represent an arc without polygon closure semantics. nVertices < 4 falls back to DefaultBufferSegments.
func (Circle) Circumference ¶ added in v0.3.1
Circumference returns 2πr.
func (Circle) Contains ¶ added in v0.3.1
Contains reports whether p lies inside (or on the boundary of) the circle. Uses the Euclidean distance from p to the center. Only the 2D X/Y coordinates are considered.
type CircleFitMethod ¶ added in v0.3.1
type CircleFitMethod int
CircleFitMethod selects the least-squares algorithm.
const ( // FitTaubin (default): geometrically-weighted algebraic fit // (Taubin 1991 / Chernov). Closed-form via one Newton step on a // cubic; unbiased when the point cloud spans only a partial arc. // Preferred for GIS-style fits where points may cluster on one // side of the true circle. FitTaubin CircleFitMethod = iota // FitKasa: plain algebraic fit (Kasa 1976). Faster (linear // solve, no root-find) but biased toward smaller radii on // partial-arc inputs. Use when speed dominates and inputs are // known to span most of the circumference. FitKasa )
type CircleFitOptions ¶ added in v0.3.1
type CircleFitOptions struct {
Method CircleFitMethod
}
CircleFitOptions tunes FitCircle behavior. The zero value picks sensible defaults.
type ClipOptions ¶ added in v0.3.0
type ClipOptions struct {
// Tolerance controls when two coordinates are treated as coincident.
// Comparisons scale by max(|x|, |y|), so this is a relative tolerance.
// Zero picks DefaultClipTolerance.
Tolerance float64
}
ClipOptions tunes the boolean-op engine. The zero value picks sensible defaults; callers rarely need to override.
type Ellipse ¶ added in v0.3.3
type Ellipse struct {
Center Point
SemiA float64 // "first" semi-axis (along +X in ellipse-local frame)
SemiB float64 // "second" semi-axis (along +Y in ellipse-local frame)
Rotation float64 // CCW radians from +X to ellipse-local +X
}
Ellipse is an axis-aligned-then-rotated elliptical shape. Like Circle it is deliberately NOT a Geometry — OGC SFA / WKB has no ellipse encoding, so callers who need to serialize should lower via Boundary(n) → Polygon or BoundaryLine(n) → LineString.
Rotation is measured in radians, counter-clockwise from the +X axis. When Rotation == 0, SemiA runs along +X and SemiB along +Y. Units follow Center.CRSValue (meters for UTM, degrees for WGS84); for physically-meaningful ellipses on the ground, reproject the input via ToCRS to a projected CRS first.
func EllipseFromFoci ¶ added in v0.3.3
EllipseFromFoci constructs the unique ellipse with the two given foci and a specified major-axis length. Requires majorAxis >= |f1 - f2| (otherwise no ellipse satisfies the definition). SemiA is placed along the f1→f2 direction; SemiB perpendicular.
Both foci must share (or leave unset) the same CRS; the output Ellipse inherits it from f1.
func NewEllipse ¶ added in v0.3.3
NewEllipse returns an Ellipse with the given center, semi-axes, and rotation. Semi-axis order is not enforced: callers may pass SemiA < SemiB (rotation still applies).
func (Ellipse) Boundary ¶ added in v0.3.3
Boundary returns a closed CCW Polygon approximating the ellipse with nVertices segments. Each vertex is the parametric point (a cos t, b sin t) transformed by Rotation and translated to Center. Points nVertices < 4 fall back to DefaultBufferSegments (matching Circle.Boundary and Point.Buffer for consistency).
func (Ellipse) BoundaryLine ¶ added in v0.3.3
func (e Ellipse) BoundaryLine(nVertices int) LineString
BoundaryLine returns an open LineString along the ellipse's circumference (nVertices points, NOT closed — the last point is not a repeat of the first). Useful for representing an arc without polygon closure semantics.
func (Ellipse) Bounds ¶ added in v0.3.3
Bounds returns the axis-aligned bounding box of the rotated ellipse. Derived from the parametric extremum:
x_max = √(a²cos²θ + b²sin²θ) y_max = √(a²sin²θ + b²cos²θ)
where θ is the rotation. The box is centered on e.Center and has full width 2·x_max, full height 2·y_max.
func (Ellipse) Circumference ¶ added in v0.3.3
Circumference returns an approximation via Ramanujan's second formula:
C ≈ π(a+b) · (1 + 3h / (10 + √(4 - 3h))) where h = ((a-b)/(a+b))²
Accurate to ~1e-9 for eccentricity < 0.9 (i.e. axis ratio > 0.44), degrading to ~1e-4 near extreme eccentricity. No closed-form exact solution exists for the ellipse's perimeter (it's an incomplete elliptic integral of the second kind).
type Geometry ¶
type Geometry interface {
// Type returns the concrete geometry type.
Type() Type
// CRS returns the coordinate reference system.
CRS() CRS
// Bounds returns the axis-aligned bounding box (minX, minY, maxX, maxY).
Bounds() Bounds
// Is3D reports whether the geometry carries Z (XYZ). Bounds and 2D
// operations ignore Z whether or not the geometry is 3D.
Is3D() bool
// Centroid returns the geometry's centroid using its type-specific
// definition. For Point this is the identity; for lines, polygons,
// and collections see the concrete implementations.
Centroid() Point
// WKT returns the Well-Known Text representation.
WKT() string
// AppendWKB appends the little-endian WKB encoding to buf and returns
// the resulting slice.
AppendWKB(buf []byte) []byte
}
Geometry is the common interface for all geometry primitives.
func Boolean ¶ added in v0.3.0
func Boolean(a, b Geometry, op BoolOp, opts ClipOptions) (Geometry, error)
Boolean applies op to a and b using the given options. Boolean is the shared entry point that Clip/Union/Difference/SymDifference route through.
func Buffer ¶
func Buffer(g Geometry, distance float64, opts BufferOptions) (Geometry, error)
Buffer returns a polygon (or MultiPolygon-shaped result) approximating the set of points within distance of g. The Style option in opts picks between rounded (default) and square caps/joins.
Only positive distances are supported. Passing distance <= 0 returns (nil, error). Buffering Polygon inputs assumes the input's exterior ring is convex or nearly so; strongly concave inputs may produce self- intersecting output rings (see file-level comment).
func Clip ¶ added in v0.3.0
Clip returns the geometric intersection of subject and mask. Both must be Polygon or MultiPolygon in the same projected CRS. Non-polygonal inputs return an error. An empty result is a Polygon with no rings.
func Difference ¶ added in v0.3.0
Difference returns a minus b (points in a that are not in b). See Clip for input rules.
func Dissolve ¶ added in v0.3.0
Dissolve merges every polygon in geoms into their union. Inputs may be Polygon or MultiPolygon; other geometry types return an error. All inputs must agree on CRS (an unset CRS is treated as a wildcard).
The implementation clusters inputs by bbox connectivity using an R-tree, then unites each cluster via a spatially-sorted divide-and-conquer merge (like shapely's unary_union). On fully-disjoint inputs this is near-linear; on heavily-overlapping inputs it's O(n log n) sweeps.
func ParseWKB ¶
ParseWKB decodes a Well-Known Binary geometry. The resulting geometry has no CRS set; callers must supply one from context (e.g. a GeoParquet schema).
func ParseWKT ¶
ParseWKT parses a Well-Known Text geometry. Whitespace and case around the type keyword are tolerated; a " Z" qualifier after the keyword (e.g. "POINT Z (1 2 3)") switches the parser into 3D mode. Coordinates must be numeric.
func Project ¶
Project reprojects g from its current CRS to target. If the source or target CRS is unset, WGS84 is assumed. Supported source/target pairs:
- WGS84 (EPSG:4326) ↔ Web Mercator (EPSG:3857)
- WGS84 (EPSG:4326) ↔ UTM zone (EPSG:32601–32660 / 32701–32760)
Other pairs (Mercator ↔ UTM, UTM ↔ UTM) are routed through WGS84 internally.
func Simplify ¶
Simplify returns a copy of g with vertices removed until every discarded vertex lies within tolerance (planar distance) of the retained polyline. Uses the Douglas-Peucker algorithm.
tolerance is measured in the CRS's linear unit (degrees for WGS84, meters for a projected CRS). Passing tolerance <= 0 returns g unchanged.
Point and MultiPoint pass through untouched; the algorithm doesn't apply to them. GeometryCollection recurses into each component.
func SplitAtAntimeridian ¶ added in v0.3.0
SplitAtAntimeridian returns g split into components that each lie on one side of the ±180° meridian. Antimeridian-crossing edges get a synthetic vertex inserted at (±180, interpolated-lat) on each side. Non-crossing inputs pass through unchanged.
Supported types: LineString → MultiLineString, MultiLineString → MultiLineString, Polygon → MultiPolygon (assuming the exterior ring crosses an even number of times and holes don't cross), MultiPolygon → MultiPolygon. Point / MultiPoint pass through unchanged. GeometryCollection recurses into components.
Returns an error only if the input CRS is projected (where the operation is meaningless) — in that case the geometry is returned unchanged.
func SymDifference ¶ added in v0.3.0
SymDifference returns the symmetric difference of a and b (points in exactly one of them). See Clip for input rules.
type GeometryCollection ¶
GeometryCollection is a heterogeneous set of geometries. Per the OGC spec and PostGIS's practical guarantee, a GeometryCollection may not directly contain another GeometryCollection. Setting HasZ marks the collection as 3D; each contained geometry is expected to share that dimensionality.
func NewGeometryCollection ¶
func NewGeometryCollection(gs []Geometry, crs CRS) GeometryCollection
NewGeometryCollection returns a 2D GeometryCollection wrapping the given geometries.
func NewGeometryCollectionZ ¶
func NewGeometryCollectionZ(gs []Geometry, crs CRS) GeometryCollection
NewGeometryCollectionZ returns a 3D GeometryCollection.
func (GeometryCollection) AppendWKB ¶
func (c GeometryCollection) AppendWKB(buf []byte) []byte
func (GeometryCollection) Bounds ¶
func (c GeometryCollection) Bounds() Bounds
func (GeometryCollection) CRS ¶
func (c GeometryCollection) CRS() CRS
func (GeometryCollection) Centroid ¶
func (c GeometryCollection) Centroid() Point
Centroid returns the midpoint of the collection's overall bounding box. A meaningful centroid for a heterogeneous collection is not universally defined; the bounds midpoint is a stable, cheap approximation.
func (GeometryCollection) EstimateUTMCRS ¶
func (c GeometryCollection) EstimateUTMCRS() (CRS, error)
EstimateUTMCRS returns the CRS of the UTM zone covering the collection's bounds midpoint.
func (GeometryCollection) Is3D ¶
func (c GeometryCollection) Is3D() bool
func (GeometryCollection) Type ¶
func (c GeometryCollection) Type() Type
func (GeometryCollection) WKT ¶
func (c GeometryCollection) WKT() string
type LineString ¶
LineString is an ordered sequence of two or more points. Set HasZ to true to encode Z values in WKB/WKT output.
func DensifyGeodesic ¶ added in v0.3.2
func DensifyGeodesic(l LineString, stepMeters float64) (LineString, error)
DensifyGeodesic replaces every segment of l with its great-circle densification at ≤ stepMeters spacing. Endpoints of each original segment are always preserved. Requires l to be in a geographic CRS (see SampleGeodesic).
stepMeters is measured on a sphere of Earth radius (matching the existing Haversine implementation). Values <= 0 return the input unchanged.
func NewLineString ¶
func NewLineString(pts []Point, crs CRS) LineString
NewLineString returns a 2D LineString sharing the underlying slice.
func NewLineStringZ ¶
func NewLineStringZ(pts []Point, crs CRS) LineString
NewLineStringZ returns a 3D LineString.
func (LineString) AppendWKB ¶
func (l LineString) AppendWKB(buf []byte) []byte
func (LineString) Bounds ¶
func (l LineString) Bounds() Bounds
Bounds returns the XY bounding box. Z is ignored.
func (LineString) Buffer ¶
func (l LineString) Buffer(distance float64, segments int) Polygon
Buffer returns a polygon approximating the set of points within distance of the linestring, using rounded joins at internal vertices and rounded caps at the endpoints. Requires the linestring to have at least 2 points.
func (LineString) CRS ¶
func (l LineString) CRS() CRS
func (LineString) Centroid ¶
func (l LineString) Centroid() Point
Centroid returns the length-weighted midpoint of the linestring in planar XY. Each segment contributes its midpoint weighted by its planar length; the result carries l's CRS.
func (LineString) Clip ¶ added in v0.3.12
func (l LineString) Clip(p Polygon) []LineString
Clip returns the sub-LineStrings of l that lie inside p (inclusive of the polygon boundary). Fragments preserve order along l: fragment i occurs before fragment i+1 when walking l from its first to last vertex.
Contract:
- l fully inside p -> []LineString{l}
- l fully outside p -> nil
- l touches p at a single vertex -> nil (zero-length overlap dropped)
- l coincident with a p edge -> the coincident sub-segment is returned as one fragment (convex path only; the concave/holed path treats boundary-coincident sub-segments as unspecified — a follow-up will tighten this)
Coordinate system: planar (WGS84 lon/lat treated as x/y). Callers that need geodesic accuracy should densify l first. Antimeridian handling is the caller's responsibility (see SplitAtAntimeridian).
func (LineString) EstimateUTMCRS ¶
func (l LineString) EstimateUTMCRS() (CRS, error)
EstimateUTMCRS returns the CRS of the UTM zone covering the linestring's bounds midpoint. If the linestring is in a projected CRS, the midpoint is inverse-projected to WGS84 first.
func (LineString) Is3D ¶
func (l LineString) Is3D() bool
func (LineString) Length ¶
func (l LineString) Length(u Unit) (float64, error)
Length returns the total planar (XY) length of the linestring in the requested unit. Z is ignored. Geographic CRSes use haversine; projected CRSes use planar distance.
func (LineString) Simplify ¶
func (l LineString) Simplify(tolerance float64) LineString
Simplify returns a copy of l with vertices removed via Douglas-Peucker at the given planar tolerance. Endpoints are always preserved. If the line has fewer than 3 points it is returned unchanged (a 2-point line is already the simplest possible representation).
func (LineString) SplitBy ¶ added in v0.3.12
func (l LineString) SplitBy(p Polygon) (inside, outside []LineString)
SplitBy returns the fragments of l that lie inside p and the fragments that lie outside p, in a single pass. Both slices preserve order along l. Equivalent to Clip(p) plus a complementary clip, but shares the per-segment edge-intersection work.
func (LineString) ToCRS ¶
func (l LineString) ToCRS(target CRS) (LineString, error)
ToCRS reprojects the linestring into target.
func (LineString) Type ¶
func (l LineString) Type() Type
func (LineString) View ¶ added in v0.4.1
func (l LineString) View() PointsView
View materializes a PointsView from a LineString's ordered points. Empty input yields a non-nil empty view.
func (LineString) WKT ¶
func (l LineString) WKT() string
type MultiLineString ¶
type MultiLineString struct {
Lines []LineString
CRSValue CRS
HasZ bool
}
MultiLineString is a collection of LineStrings.
func NewMultiLineString ¶
func NewMultiLineString(lines []LineString, crs CRS) MultiLineString
NewMultiLineString returns a 2D MultiLineString wrapping lines.
func NewMultiLineStringZ ¶
func NewMultiLineStringZ(lines []LineString, crs CRS) MultiLineString
NewMultiLineStringZ returns a 3D MultiLineString.
func (MultiLineString) AppendWKB ¶
func (m MultiLineString) AppendWKB(buf []byte) []byte
func (MultiLineString) Bounds ¶
func (m MultiLineString) Bounds() Bounds
func (MultiLineString) CRS ¶
func (m MultiLineString) CRS() CRS
func (MultiLineString) Centroid ¶
func (m MultiLineString) Centroid() Point
Centroid returns the length-weighted centroid of the multi-linestring, where each component contributes its centroid weighted by its planar length. Empty geometry returns the zero Point.
func (MultiLineString) Clip ¶ added in v0.3.12
func (m MultiLineString) Clip(p Polygon) []LineString
Clip returns the sub-LineStrings of m that lie inside p (inclusive of the polygon boundary). Fragments preserve two orderings: fragments from component i appear before fragments from component i+1, and within each component they appear in walk order. See LineString.Clip for the per-component edge-case contract.
func (MultiLineString) EstimateUTMCRS ¶
func (m MultiLineString) EstimateUTMCRS() (CRS, error)
EstimateUTMCRS returns the CRS of the UTM zone covering the multiline's bounds midpoint.
func (MultiLineString) Is3D ¶
func (m MultiLineString) Is3D() bool
func (MultiLineString) Length ¶
func (m MultiLineString) Length(u Unit) (float64, error)
Length sums the length of every line in the requested unit (XY only).
func (MultiLineString) LineViews ¶ added in v0.4.1
func (m MultiLineString) LineViews() []PointsView
LineViews materializes one PointsView per LineString in a MultiLineString. Each view carries the multi's CRS + HasZ.
func (MultiLineString) SplitBy ¶ added in v0.3.12
func (m MultiLineString) SplitBy(p Polygon) (inside, outside []LineString)
SplitBy returns the fragments of m that lie inside p and the fragments that lie outside p. Both slices preserve component-and-walk order: component i's fragments appear before component i+1's, and within a component fragments appear in walk order.
func (MultiLineString) Type ¶
func (m MultiLineString) Type() Type
func (MultiLineString) WKT ¶
func (m MultiLineString) WKT() string
type MultiPoint ¶
MultiPoint is an unordered collection of points. Set HasZ to true to encode Z on WKB/WKT output.
func NewMultiPoint ¶
func NewMultiPoint(pts []Point, crs CRS) MultiPoint
func NewMultiPointZ ¶
func NewMultiPointZ(pts []Point, crs CRS) MultiPoint
func (MultiPoint) AppendWKB ¶
func (m MultiPoint) AppendWKB(buf []byte) []byte
func (MultiPoint) Bounds ¶
func (m MultiPoint) Bounds() Bounds
func (MultiPoint) CRS ¶
func (m MultiPoint) CRS() CRS
func (MultiPoint) Centroid ¶
func (m MultiPoint) Centroid() Point
Centroid returns the arithmetic mean of the multipoint's points.
func (MultiPoint) EstimateUTMCRS ¶
func (m MultiPoint) EstimateUTMCRS() (CRS, error)
EstimateUTMCRS returns the CRS of the UTM zone covering the multipoint's bounds midpoint.
func (MultiPoint) Is3D ¶
func (m MultiPoint) Is3D() bool
func (MultiPoint) Type ¶
func (m MultiPoint) Type() Type
func (MultiPoint) View ¶ added in v0.4.1
func (m MultiPoint) View() PointsView
View materializes a PointsView from a MultiPoint's points. Order follows MultiPoint.Points.
func (MultiPoint) WKT ¶
func (m MultiPoint) WKT() string
type MultiPolygon ¶
MultiPolygon is a collection of Polygons.
func NewMultiPolygon ¶
func NewMultiPolygon(polys []Polygon, crs CRS) MultiPolygon
NewMultiPolygon returns a 2D MultiPolygon wrapping polys.
func NewMultiPolygonZ ¶
func NewMultiPolygonZ(polys []Polygon, crs CRS) MultiPolygon
NewMultiPolygonZ returns a 3D MultiPolygon.
func (MultiPolygon) AppendWKB ¶
func (m MultiPolygon) AppendWKB(buf []byte) []byte
func (MultiPolygon) Area ¶
func (m MultiPolygon) Area(u Unit) (float64, error)
Area sums the planar (XY) area of every component polygon in u².
func (MultiPolygon) Bounds ¶
func (m MultiPolygon) Bounds() Bounds
func (MultiPolygon) CRS ¶
func (m MultiPolygon) CRS() CRS
func (MultiPolygon) Centroid ¶
func (m MultiPolygon) Centroid() Point
Centroid returns the area-weighted centroid of the multipolygon. Each component contributes its own centroid weighted by its planar area (or spherical area for geographic CRSes). Empty geometry returns the zero Point.
func (MultiPolygon) Contains ¶
func (m MultiPolygon) Contains(pt Point) bool
Contains reports whether pt lies inside any polygon in the collection.
func (MultiPolygon) EstimateUTMCRS ¶
func (m MultiPolygon) EstimateUTMCRS() (CRS, error)
EstimateUTMCRS returns the CRS of the UTM zone covering the multipolygon's bounds midpoint.
func (MultiPolygon) Is3D ¶
func (m MultiPolygon) Is3D() bool
func (MultiPolygon) PolygonRingViews ¶ added in v0.4.1
func (m MultiPolygon) PolygonRingViews() [][]PointsView
PolygonRingViews materializes one []PointsView per Polygon in a MultiPolygon. Outer slice index selects the polygon; inner slice mirrors that polygon's ring layout (exterior first, then holes).
func (MultiPolygon) Type ¶
func (m MultiPolygon) Type() Type
func (MultiPolygon) WKT ¶
func (m MultiPolygon) WKT() string
type Point ¶
Point is a 2D or optionally 3D (XYZ) point. Z is populated only when HasZ is true; otherwise it is ignored by encoders and decoders.
func AntimeridianCrossings ¶ added in v0.3.0
AntimeridianCrossings returns the (lon=±180, lat) points at which g's boundary crosses the antimeridian, in the order encountered while walking edges. Each physical crossing produces ONE entry (at lon matching the edge's starting side); callers building split output should mirror to the opposite side themselves.
func Centroid ¶
Centroid returns the centroid of g using each concrete type's own definition. Kept as a package-level helper for API symmetry with Area and Length; internally this is just interface dispatch on g.Centroid().
func CentroidFromWKB ¶ added in v0.4.1
CentroidFromWKB extracts the geometry's centroid from a WKB blob without materializing intermediate `[]Point` / `Polygon` / etc. structs. Walks the byte stream once, running per-type centroid accumulators against the raw coordinate pairs.
This is Slice 3's SoA fast path for the `SortByHilbert` write path. The two-pass `SortByHilbertWith` and the fused `HilbertSortWithCovering` both currently parse every row's WKB into a full geometry, call `.Centroid()`, and discard the geometry — exactly the shape BoundsFromWKB (Slice 2) already targets on the bbox side.
Semantics vs g.Centroid() ¶
The returned Point matches `ParseWKB(data).Centroid()` on Point, LineString, Polygon, MultiPoint, and MultiLineString. Divergences:
MultiPolygon centroid uses bbox-center. The AoS `MultiPolygon.Centroid()` weights each sub-polygon by its geodesic area (`Area(UnitMeters)`), which requires CRS context this scanner doesn't carry from the WKB alone. bbox-center is locality-preserving and CRS-independent — enough for spatial-sort use cases (Hilbert-index inputs) which don't care about geodesic accuracy.
GeometryCollection centroid uses bbox-center. This matches the AoS implementation exactly (see collection.go — `GeometryCollection.Centroid()` already returns bbox-center).
The returned Point's CRS is unset — the WKB blob doesn't carry CRS. Callers embedding CRS via a schema/annotation must set it themselves.
Zero-allocation on well-formed input.
func CircleIntersectionPoints ¶ added in v0.3.3
CircleIntersectionPoints returns the points at which the boundaries of c1 and c2 cross:
- two points for two circles that properly overlap
- one point when the circles are externally or internally tangent
- zero points when the circles are disjoint, one properly contains the other, or the circles are concentric (in which case the "intersection" is either empty or the whole circle, neither of which is representable as a finite point set)
Result points inherit CRS from c1.Center.
func NewPoint ¶
NewPoint returns a 2D Point with the given coordinates and CRS. Passing the zero CRS leaves the CRS unset (interpreted as WGS84 by most operations).
func SampleGeodesic ¶ added in v0.3.2
SampleGeodesic returns n points along the great-circle arc from a to b (inclusive of both endpoints). n must be >= 2. Both points must be in a geographic CRS with X = longitude and Y = latitude in degrees. Uses standard spherical linear interpolation (slerp) on unit sphere vectors — no ellipsoidal-Earth corrections.
Output points carry the input's CRS (or WGS84 if the CRS is unset) and lon in [-180, 180].
func (Point) Bounds ¶
Bounds returns the XY bounding box. Z, if present, is ignored (the Bounds type is deliberately 2D).
func (Point) Buffer ¶
Buffer returns a circular polygon of radius distance centered on p, approximated with the given number of edge segments. segments < 4 falls back to DefaultBufferSegments.
func (Point) Centroid ¶
Centroid returns p itself — a Point is its own centroid. Present so Point satisfies the Geometry interface's Centroid method and the top-level geometry.Centroid dispatch collapses to interface dispatch.
func (Point) Distance ¶
Distance returns the planar (XY) distance from p to o in the requested unit. Z is ignored. For geographic CRSes Haversine is used; for projected CRSes Euclidean.
func (Point) Distance3D ¶
Distance3D returns the 3D Euclidean distance from p to o, treating Z as coplanar with X/Y (i.e. as if measured in the same unit as the CRS's linear unit). Requires both points to be in the same projected CRS and to have HasZ set on both sides; otherwise returns ErrCRSMismatch or an error noting the missing dimension.
func (Point) Equal ¶
Equal reports whether p and o are equal in coordinates, CRS, and dimensionality. Z is compared only if both points are 3D.
func (Point) EstimateUTMCRS ¶
EstimateUTMCRS returns the CRS of the UTM zone covering p (on the WGS84 datum). If p is in a projected CRS, it is first inverse-projected to WGS84 to pick the zone.
type PointsView ¶ added in v0.4.1
type PointsView struct {
// Xs and Ys are parallel arrays of the same length holding the
// point coordinates. Never nil for a materialized view — an
// empty geometry produces an empty (zero-length, non-nil) slice.
Xs, Ys []float64
// Zs holds Z coordinates when HasZ is true. Nil otherwise —
// callers that don't check HasZ but iterate Zs get an obvious
// panic rather than reading zeros as valid altitudes.
Zs []float64
// HasZ mirrors the source geometry's Is3D() at view time.
HasZ bool
// CRS is the coordinate reference system for the whole view.
// In the AoS `[]Point` representation this is duplicated per
// point; SoA carries it once at the collection level.
CRS CRS
}
PointsView is a struct-of-arrays view over a sequence of 2D or 3D points sharing a single CRS. Callers who need SoA-friendly access (bbox compute, Hilbert index, SIMD-eligible loops) call `.View()` on the source geometry to materialize a fresh view, then work on the parallel `Xs` / `Ys` slices directly.
Amortization ¶
Materialization is O(n) per `.View()` call — a fresh allocation of parallel float64 slabs plus a struct-to-slabs copy loop. Do NOT call `.View()` for a single operation; the fresh conversion costs more than the AoS-form equivalent. Benchmarks on LineString.Bounds (n=1M): AoS = 1.55 ms, SoA on held view = 1.10 ms (−29%), SoA with fresh view = 3.25 ms (+109%). Rule of thumb: break-even is roughly two SoA operations per view.
The default `.Bounds()` / `.Contains()` / etc. methods on LineString / Polygon / MultiPolygon deliberately keep the AoS path so single-op callers don't regress. Downstream slices (WKB parse-into-view, Hilbert index, spatial predicates) offer entry points that skip the AoS intermediate entirely on the read-heavy paths where amortization actually holds.
func LineStringViewFromWKB ¶ added in v0.4.1
func LineStringViewFromWKB(data []byte) (PointsView, error)
LineStringViewFromWKB parses a LineString or LineStringZ WKB directly into a PointsView, skipping the intermediate []Point slice that ParseWKB(data).View() would allocate. Type codes other than LineString / LineStringZ return ErrTypeMismatch.
This is the WKB-facing sibling of LineString.View() for callers materializing SoA views from bytes (Hilbert index over stored WKB, spatial-predicate refine loops, streamed-parquet consumers). Two allocations: the Xs and Ys slabs sized to the vertex count. One extra Zs slab when the WKB carries Z.
CRS is left unset on the returned view — the WKB blob doesn't carry CRS. Callers embedding CRS via schema/annotation must attach it themselves.
func PolygonRingViewsFromWKB ¶ added in v0.4.1
func PolygonRingViewsFromWKB(data []byte) ([]PointsView, error)
PolygonRingViewsFromWKB parses a Polygon or PolygonZ WKB directly into a []PointsView with one view per ring (exterior at index 0, then holes). Skips the ParseWKB intermediate [][]Point allocation.
This is the primary Slice-10 entry point for PreparedGeometry callers — collapses ParseWKB + Polygon.RingViews() into a single byte-stream pass. Wired into PrepareFromWKB.
func (PointsView) Bounds ¶ added in v0.4.1
func (v PointsView) Bounds() Bounds
Bounds returns the 2D axis-aligned bounding box of the view. Delegates to BoundsFromXY on Xs and Ys — Z, if present, is not part of the 2D bounds (matches the Bounds type's XY-only shape).
func (PointsView) ConvexHull ¶ added in v0.4.1
func (v PointsView) ConvexHull() PointsView
ConvexHull materializes the convex hull of v as a fresh PointsView with the same CRS/HasZ. Z coordinates (when v.HasZ) are copied for retained indices — the hull decision uses XY only, matching the Polygon.ConvexHull shape.
This is the amortized-view entry point for callers holding a materialized PointsView (via LineString.View(), Polygon.RingViews(), etc.) — no AoS []Point round-trip.
func (PointsView) Len ¶ added in v0.4.1
func (v PointsView) Len() int
Len returns the number of points in the view.
func (PointsView) SimplifyDP ¶ added in v0.4.1
func (v PointsView) SimplifyDP(tolerance float64) PointsView
SimplifyDP applies Douglas-Peucker to the coordinates held by v, returning a new PointsView with the same CRS and HasZ. Z coordinates (when v.HasZ) are copied for retained indices — the split decisions use XY only, matching the AoS shape.
This is the amortized-view entry point: callers holding a materialized PointsView (via LineString.View() or Polygon.RingViews()) can simplify without going through the AoS []Point round-trip.
type Polygon ¶
Polygon is a planar surface defined by one exterior linear ring and zero or more interior rings (holes). Rings are lists of points; the first and last point of each ring must coincide (Polygon operations close rings implicitly if they don't).
func ConvexHull ¶ added in v0.3.0
ConvexHull returns the convex hull of every vertex in g as a Polygon. Handles all concrete geometry types by extracting vertices and running a Graham scan over the union. Returns an empty Polygon if g has fewer than 3 unique vertices.
func Envelope ¶ added in v0.3.0
Envelope returns the axis-aligned bounding-box polygon of g. Matches geopandas's GeoSeries.envelope: 5-vertex closed ring (MinXY, MaxX/MinY, MaxXY, MinX/MaxY, close). Empty input returns a zero-ring Polygon.
func LensPolygon ¶ added in v0.3.3
LensPolygon returns the intersection region of c1 and c2 as a Polygon, sampled analytically (no sweep-line involved). arcSegments is the number of samples PER arc — the returned ring has ~2· arcSegments vertices. Values < 4 fall back to DefaultBufferSegments / 2.
Special cases:
- Disjoint circles → empty Polygon (Rings == nil).
- One circle properly contains the other → the smaller circle's Boundary (the lens IS the smaller circle).
- Concentric circles with equal radius → the shared circle's Boundary.
- Tangent circles → empty Polygon (a lens with zero area).
Output is wound CCW (as gobi's convention prefers for exterior rings) and inherits CRS from c1.Center.
func NewPolygon ¶
NewPolygon returns a 2D Polygon with the given rings. The first ring is the exterior boundary; any subsequent rings are holes.
func NewPolygonZ ¶
NewPolygonZ returns a 3D Polygon.
func SimplePolygon ¶
SimplePolygon returns a Polygon with a single exterior ring.
func (Polygon) Area ¶
Area returns the polygon area. For geographic CRSes the area is computed on a sphere of Earth radius and returned in u² (u ∈ km/mi/nmi/m/ft). For projected CRSes the area is planar (signed absolute value) in the CRS's linear unit², converted to u².
func (Polygon) Buffer ¶
Buffer returns a polygon approximating the set of points within distance of p's exterior ring. Holes are shrunk (their offset ring moves inward); if a hole's offset would collapse or self-intersect it is dropped.
This is a positive (outward) buffer only. For convex or nearly-convex inputs the output is topologically correct. For strongly concave inputs the offset exterior ring may self-intersect — cleaning that up requires a polygon-union pass, which this package doesn't yet provide.
func (Polygon) Centroid ¶
Centroid returns the area-weighted centroid of the exterior ring, using the planar (shoelace) formula. For small geographic polygons the result is a close approximation.
func (Polygon) Contains ¶
Contains reports whether pt lies inside p (exterior ring, minus any holes). The check uses the winding-parity (crossing) rule. Points on the boundary have undefined containment.
func (Polygon) ConvexHull ¶
ConvexHull returns the convex hull of the polygon's exterior points. Collinear points on hull edges are dropped.
Slice-12 SoA rewrite: extracts the exterior into parallel Xs / Ys slabs and runs Andrew's monotone chain (ConvexHullFromXY), replacing the AoS Graham scan's polar-angle sort on []Point. Sorting indices (8-byte swaps) instead of Point structs (40-byte swaps) with coord reads from cache-friendly float64 slabs cuts wall time meaningfully on n≥1K exteriors.
The result's starting vertex may differ from the pre-Slice-12 Graham output (Andrew starts at leftmost-then-lowest-Y; Graham started at lowest-Y-then-lowest-X) — the vertex SET and CCW ordering are the same.
func (Polygon) EstimateUTMCRS ¶
EstimateUTMCRS returns the CRS of the UTM zone covering the polygon's centroid. If the polygon is in a projected CRS, the centroid is inverse-projected to WGS84 first.
func (Polygon) IsConvex ¶ added in v0.3.0
IsConvex reports whether p is a single-ring polygon whose vertices wind consistently (all left-turns or all right-turns). Polygons with holes always return false (a hole makes the polygon non-convex). Zero-area rings return false.
func (Polygon) RingViews ¶ added in v0.4.1
func (p Polygon) RingViews() []PointsView
RingViews materializes one PointsView per ring in a Polygon. Rings[0] is the exterior; subsequent rings are holes. Each view carries the polygon's CRS + HasZ. Empty Polygon returns nil.
func (Polygon) Simplify ¶
Simplify applies Douglas-Peucker to each ring of the polygon. Rings that collapse to fewer than 4 points (three unique vertices plus the closing vertex) are kept as-is to preserve topological validity of the polygon.
type Predicate ¶
type Predicate uint8
Predicate names a binary spatial predicate.
const ( // PredIntersects: a and b share any point. PredIntersects Predicate = iota // PredContains: a fully contains b (every point of b lies in a). PredContains // PredWithin: a lies fully within b (equivalent to Contains(b, a)). PredWithin // PredTouches: a and b share boundary points but no interior // points. Matches DE-9IM T*F**F*** / F*T**F*** / F**T*F***. PredTouches // PredCrosses: a and b share some but not all interior points and // the dimension of the intersection is less than max(dim(a), dim(b)). // Typical shape: LineString × Polygon or LineString × LineString // with mixed dimensions. PredCrosses // PredOverlaps: a and b share some interior points, neither // contains the other, and both are of the same dimension. PredOverlaps // PredDisjoint: a and b share no point. Exactly the negation of // PredIntersects; kept as its own value so downstream code // dispatches on n.pred alone rather than an "intersects + invert" // side-channel (which is easy to trip on when adding new logic). PredDisjoint )
type PreparedGeometry ¶ added in v0.4.1
type PreparedGeometry struct {
// G is the source geometry. Retained so the fallback Test(G, other)
// path stays available for shapes without a fast path.
G Geometry
// Bounds is g.Bounds() cached at Prepare time. Used for the
// cheap bbox-reject step in TestPrepared without a repeat
// Bounds() call per predicate evaluation.
Bounds Bounds
// contains filtered or unexported fields
}
PreparedGeometry wraps a Geometry with precomputed indexes + cached bounds for accelerated repeated predicate evaluation. The primary use case is spatial joins where each right-side polygon is tested against many left-side candidate points — caching the indexes once amortizes the per-call setup tax across every predicate call.
When to use ¶
Prepare + TestPrepared wins on hot loops where the amortization ratio — number of predicate calls per prepared geometry — exceeds the break-even point measured on the target shape. The amortized-view microbench in pip_bench_test.go shows a 3× win on a 64-vertex polygon with 100 candidate points held-view; the break-even is around 5-10 candidates per polygon for that shape.
Cost model ¶
- Polygon: Prepare materializes ring PointsViews upfront (2 allocs per ring). Query cost is one PIP walk per call.
- MultiPolygon: Prepare caches one Bounds per sub-polygon (single small slab) and — when N ≥ 16 — builds an R-tree over those bboxes. Per-sub-polygon ring views are populated LAZILY on first hit (atomic publish; concurrent readers race benignly). Query cost is O(log N + k) via the tree, or O(N) bbox-compares + k PIPs via the linear scan.
The lazy MultiPolygon path is what makes Prepare(landMP) with hundreds of small islands cheap: only bbox slabs are allocated upfront (24 bytes × N), and only sub-polygons that a query actually touches ever pay their ring-view cost. The pre-review implementation materialized every ring of every sub-poly at Prepare time, which was a strict regression on many-small-poly shapes because the upfront work dwarfed the per-query savings.
Fast paths ¶
The SoA fast paths kick in for the following pair shapes, covering the dominant spatial-join workloads:
- Point × Polygon (both orderings) — PredContains, PredIntersects, PredWithin. Uses PIPPolygonFromRings on the polygon's cached ring views. Boundary points fall through to the AoS path via pointOnPolygonBoundary so results match Test() exactly.
- Point × MultiPolygon (both orderings) — same predicates. Tree-indexed (N ≥ 16) or linear-with-bbox-reject (N < 16), then per-candidate PIP with lazy ring materialization.
Every other shape (Point×Point, LineString×Polygon, Polygon×Polygon, etc.) transparently falls through to Test(a.G, b.G) so callers can pass a PreparedGeometry into TestPrepared without worrying about which shapes are optimized. Adding a new fast path only requires a case in TestPrepared and matching correctness tests — no changes to callers.
gobi's built-in SJoin ¶
SJoin does NOT use PreparedGeometry today: its R-tree pre-filter on the RIGHT frame drives the candidate ratio to ~1 point per right-polygon on non-overlapping workloads, well below the amortization break-even. Callers with denser workloads (overlapping polygons, spatial-index-free refine loops, per-polygon many-candidate tests) SHOULD use PreparedGeometry directly.
func Prepare ¶ added in v0.4.1
func Prepare(g Geometry) PreparedGeometry
Prepare returns a PreparedGeometry for g.
Polygon: materializes ring views upfront. MultiPolygon: caches per-sub-polygon bounds; builds an R-tree index when N ≥ 16; leaves per-sub-polygon ring views nil for lazy on-demand materialization. Every other geometry type: bounds only. The fast-path table doesn't have entries for non-polygon geometries yet, so materializing views up front would be pure overhead.
func PrepareFromWKB ¶ added in v0.4.1
func PrepareFromWKB(data []byte) (PreparedGeometry, error)
PrepareFromWKB builds a PreparedGeometry from a WKB blob using the byte-stream direct-parse (Slice 10). Single WKB walk: the slabs are populated first, then the AoS Polygon / MultiPolygon needed for TestPrepared's non-fast-path fall-through is materialized from the slabs (a cheap float64→Point copy, not a second WKB parse). Non-polygon geometry types fall through to `Prepare(ParseWKB(data))` — they don't have cached slabs today, so there's no direct-parse win to capture.
Bounds are derived from the slabs (BoundsFromXY per ring) rather than a third byte-stream pass. Matches g.Bounds() exactly.
type RTree ¶
type RTree struct {
// contains filtered or unexported fields
}
RTree is a static, bulk-loaded Sort-Tile-Recursive R-tree over 2D bounding boxes. Once built with NewRTree the tree is immutable and safe for concurrent readers.
Internal layout: struct-of-arrays. Node bboxes live in four parallel []float64s (nodeMinX/Y/MaxX/MaxY); item bboxes get the same treatment. The query hot paths (Search, NearestOne) read tightly-packed contiguous slices instead of walking through a []struct where each 48-byte node dragged in 16 unused padding bytes per cache line.
func NewRTree ¶
NewRTree builds an R-tree over the given bounding boxes. Item IDs returned by queries are indexes into bounds.
func (*RTree) Nearest ¶
Nearest returns the k item IDs whose bounding boxes are closest (by squared Euclidean point-to-bbox distance) to (x, y), in ascending distance order. Fewer than k IDs are returned if the tree is smaller.
For the k=1 case, prefer NearestOne — same semantics but skips the priority queue for a zero-allocation depth-first descent.
func (*RTree) NearestOne ¶ added in v0.2.17
NearestOne returns the ID of the item whose bounding box is closest to (x, y) by squared Euclidean point-to-bbox distance. ok=false when the tree is empty. Semantically equivalent to Nearest(x, y, 1)[0] but with zero allocations — depth-first descent with a running best-so-far distance + bbox pruning replaces the general k>1 path's priority queue.
Callers doing a single-nearest lookup at high frequency (e.g. snap-to-graph, per-point classification) should prefer this over Nearest(x, y, 1). At 1M+ calls per request the alloc + boxing savings dominate the CPU profile.
func (*RTree) Search ¶
Search returns the IDs of every item whose bounding box intersects q. Allocates a fresh result slice per call.
func (*RTree) SearchInto ¶
SearchInto appends every item ID whose bounding box intersects q to buf (after truncating buf to zero length) and returns the resulting slice. This lets callers reuse a scratch buffer across queries to avoid a fresh allocation each time.
Source Files
¶
- antimeridian.go
- bounds.go
- buffer.go
- circle.go
- circle_intersect.go
- clip.go
- clip_connect.go
- clip_convex.go
- clip_dissolve.go
- clip_events.go
- clip_linestring.go
- clip_martinez.go
- collection.go
- crs.go
- dispatch.go
- distance.go
- distance_geom.go
- distance_view.go
- doc.go
- dwithin.go
- ellipse.go
- errors.go
- geodesic.go
- geometry.go
- hilbert.go
- hull_envelope.go
- hull_view.go
- introspect.go
- linestring.go
- multilinestring.go
- multipoint.go
- multipolygon.go
- pip.go
- point.go
- points_view.go
- polygon.go
- predicates.go
- prepared.go
- project.go
- projjson.go
- rtree.go
- simplify.go
- simplify_view.go
- topological.go
- wkb.go
- wkb_area.go
- wkb_bounds.go
- wkb_centroid.go
- wkb_distance.go
- wkb_length.go
- wkb_pip.go
- wkb_skip.go
- wkb_view.go
- wkt.go