A pure Go library providing spatial operations (Union, Intersection, Difference, SymmetricDifference, Buffer) for the Orb geometry library.
- Pure Go implementation - No C dependencies (no GEOS, etc.)
- Minimal dependencies - Only depends on
github.com/paulmach/orb - Comprehensive geometry support - Works with Points, MultiPoints, LineStrings, MultiLineStrings, Polygons, and MultiPolygons
- Five core operations:
Union- Combines two geometriesIntersection- Finds the common parts of two geometriesDifference- Subtracts one geometry from anotherSymmetricDifference- Returns parts of geometries that don't overlapBuffer- Creates a buffer zone around a geometry at a specified distance
go get github.com/tingold/orb-operationspackage main
import (
"fmt"
"github.com/paulmach/orb"
"github.com/tingold/orb-operations"
)
func main() {
// Union of two polygons
poly1 := orb.Polygon{{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}}}
poly2 := orb.Polygon{{{5, 5}, {15, 5}, {15, 15}, {5, 15}, {5, 5}}}
result := orboperations.Union(poly1, poly2)
fmt.Printf("Union result: %v\n", result)
} // Intersection of two polygons
poly1 := orb.Polygon{{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}}}
poly2 := orb.Polygon{{{5, 5}, {15, 5}, {15, 15}, {5, 15}, {5, 5}}}
result := orboperations.Intersection(poly1, poly2)
fmt.Printf("Intersection result: %v\n", result) // Difference (poly1 - poly2)
poly1 := orb.Polygon{{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}}}
poly2 := orb.Polygon{{{5, 5}, {15, 5}, {15, 15}, {5, 15}, {5, 5}}}
result := orboperations.Difference(poly1, poly2)
fmt.Printf("Difference result: %v\n", result) // Symmetric difference
poly1 := orb.Polygon{{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}}}
poly2 := orb.Polygon{{{5, 5}, {15, 5}, {15, 15}, {5, 15}, {5, 5}}}
result := orboperations.SymmetricDifference(poly1, poly2)
fmt.Printf("Symmetric difference result: %v\n", result) // Buffer a point (creates a circular polygon)
point := orb.Point{0, 0}
buffered := orboperations.Buffer(point, 5.0)
fmt.Printf("Buffered point: %v\n", buffered)
// Buffer a linestring
line := orb.LineString{{0, 0}, {10, 10}}
bufferedLine := orboperations.Buffer(line, 2.0)
fmt.Printf("Buffered line: %v\n", bufferedLine)
// Buffer a polygon (expand or contract)
poly := orb.Polygon{{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}}}
expanded := orboperations.Buffer(poly, 2.0) // Expand by 2 units
contracted := orboperations.Buffer(poly, -1.0) // Contract by 1 unit
// Buffer with custom parameters
params := orboperations.DefaultBufferParams()
params.QuadrantSegments = 16 // More segments for smoother curves
params.CapStyle = orboperations.CapSquare
params.JoinStyle = orboperations.JoinMiter
bufferedCustom := orboperations.BufferWithParams(line, 3.0, params) // Point operations
p1 := orb.Point{1, 2}
p2 := orb.Point{3, 4}
union := orboperations.Union(p1, p2) // Returns MultiPoint
intersection := orboperations.Intersection(p1, p2) // Returns MultiPoint (empty if different)
difference := orboperations.Difference(p1, p2) // Returns MultiPoint ls1 := orb.LineString{{0, 0}, {5, 5}}
ls2 := orb.LineString{{5, 5}, {10, 10}}
union := orboperations.Union(ls1, ls2) // Returns MultiLineString
intersection := orboperations.Intersection(ls1, ls2) // Returns MultiPoint (intersection points) point := orb.Point{5, 5}
poly := orb.Polygon{{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}}}
// Check if point is in polygon
intersection := orboperations.Intersection(point, poly)
if mp, ok := intersection.(orb.MultiPoint); ok && len(mp) > 0 {
fmt.Println("Point is inside polygon")
}Computes the union of two geometries. Returns a geometry representing all points that are in either geom1 or geom2.
Supported combinations:
- Point × Point → MultiPoint
- Point × MultiPoint → MultiPoint
- Point × Polygon → MultiPoint or Polygon
- MultiPoint × MultiPoint → MultiPoint
- LineString × LineString → MultiLineString
- Polygon × Polygon → Polygon
- MultiPolygon × MultiPolygon → Polygon or MultiPolygon
Computes the intersection of two geometries. Returns a geometry representing all points that are in both geom1 and geom2.
Supported combinations:
- Point × Point → MultiPoint (empty if different)
- Point × Polygon → MultiPoint (point if inside, empty otherwise)
- Polygon × Polygon → Polygon
- LineString × LineString → MultiPoint (intersection points)
Computes the difference of two geometries (geom1 - geom2). Returns a geometry representing all points that are in geom1 but not in geom2.
Computes the symmetric difference of two geometries. Equivalent to Union(Difference(geom1, geom2), Difference(geom2, geom1)).
Computes a buffer around a geometry at the specified distance. Positive distance expands the geometry, negative distance shrinks polygons. Returns a Polygon or MultiPolygon representing the buffered area.
Supported geometry types:
Point→ Creates a circular polygon approximationMultiPoint→ Buffers each point and unions the resultsLineString→ Creates a polygon around the line at the given distanceMultiLineString→ Buffers each line and unions the resultsPolygon→ Expands (positive) or contracts (negative) the polygonMultiPolygon→ Buffers each polygon and unions the resultsRing→ Treated as a closed LineStringCollection→ Buffers each element and unions the results
Computes a buffer with custom parameters. See BufferParams for available options.
Returns default buffer parameters:
QuadrantSegments: 8 (32 segments for a full circle)CapStyle:CapRound(rounded ends)JoinStyle:JoinRound(rounded corners)MiterLimit: 5.0
Cap Styles:
CapRound- Rounded ends (default)CapFlat- Flat/butt endsCapSquare- Square ends extending past endpoint
Join Styles:
JoinRound- Rounded corners (default)JoinMiter- Pointed corners (usesMiterLimit)JoinBevel- Flat corners
- Polygon operations: Uses a robust Vatti-based clipping algorithm for polygon boolean operations. The implementation combines multiple algorithms:
- Union: Uses Greiner-Hormann algorithm for robust polygon union operations
- Intersection: Uses Sutherland-Hodgman clipping algorithm
- Difference & Symmetric Difference: Uses Vatti-based approach with proper handling of complex cases
- Handles polygons with holes correctly
- LineString operations: Uses line segment intersection algorithms to find intersection points and combines segments.
- Point operations: Uses set-based operations with epsilon tolerance for floating-point comparisons.
- Point-in-polygon: Uses the ray casting algorithm.
- Buffer operations:
- Points: Creates circular polygon approximations using parametric circle generation
- LineStrings: Generates offset curves and creates polygons around line segments with configurable cap and join styles
- Polygons: Uses offset curve generation for expansion/contraction, handling both positive (expansion) and negative (contraction) distances
- Supports multiple cap styles (Round, Flat, Square) and join styles (Round, Miter, Bevel)
- LineString operations with Polygons are simplified and may not clip line strings precisely.
- Floating-point precision is handled with epsilon tolerance (1e-9).
Run the test suite:
go test ./...This library includes support for running JTS (Java Topology Suite) test fixtures to validate correctness against the industry-standard JTS implementation. This provides a high level of assurance about the correctness of the operations.
To use JTS test fixtures:
- Download JTS test fixtures from the JTS repository or GeoTools repository
- Place XML test fixture files in the
testdata/jts/directory - Run the JTS compatibility tests:
# Run all JTS tests
go test ./... -run TestJTSOperations -v
# Print summary of available test fixtures
go test ./... -run TestJTSSummary -vSee testdata/jts/README.md for detailed instructions on obtaining and using JTS test fixtures.
This project is licensed under the MIT License.
- orb - Core geometry types
- orb-predicates - Geometry predicates (Contains, Intersects, etc.)
Contributions are welcome! Please feel free to submit a Pull Request.