### Install proj dependency on Alpine Source: https://github.com/peterstace/simplefeatures/blob/master/README.md Use apk to install the proj dependency on Alpine Linux. ```bash apk add proj-dev ``` -------------------------------- ### Install proj dependency on Debian/Ubuntu Source: https://github.com/peterstace/simplefeatures/blob/master/README.md Use apt-get to install the proj dependency on Debian-based systems. ```bash apt-get install libproj-dev ``` -------------------------------- ### Install GEOS C Library Source: https://github.com/peterstace/simplefeatures/blob/master/README.md Instructions for installing the GEOS C library on Debian/Ubuntu, Alpine, and macOS systems. This is required for the `geos` CGO wrapper package. ```sh # Debian/Ubuntu apt-get install libgeos-dev # Alpine apk add geos-dev # macOS brew install geos ``` -------------------------------- ### Get PROJ Version and Perform Transformations Source: https://context7.com/peterstace/simplefeatures/llms.txt This snippet demonstrates how to get the PROJ library version, create a transformation between two CRS (EPSG:4326 and EPSG:3857), and perform forward and inverse transformations on coordinates. It also shows how to transform an entire geometry and perform 3D transformations. ```go import ( "fmt" "github.com/peterstace/simplefeatures/geom" "github.com/peterstace/simplefeatures/proj" ) major, minor, patch := proj.Version() fmt.Printf("PROJ %d.%d.%d\n", major, minor, patch) // Create a transformation — accepts EPSG codes, PROJ strings, CRS names t, err := proj.NewTransformation("EPSG:4326", "EPSG:3857") if err != nil { log.Fatal(err) } defer t.Release() // must release C resources // Forward transform: lon/lat (EPSG:4326) → Web Mercator (EPSG:3857) // Coordinates laid out as X1,Y1, X2,Y2, ... for DimXY coords := []float64{151.2093, -33.8688} // Sydney lon, lat if err := t.Forward(geom.DimXY, coords); err != nil { log.Fatal(err) } fmt.Printf("%.0f, %.0f\n", coords[0], coords[1]) // 16832930, -4006126 // Inverse transform: back to WGS84 if err := t.Inverse(geom.DimXY, coords); err != nil { log.Fatal(err) } fmt.Printf("%.4f, %.4f\n", coords[0], coords[1]) // 151.2093, -33.8688 // Transform a whole geometry using Geometry.Transform g, _ := geom.UnmarshalWKT("LINESTRING(151.2 -33.8, 144.9 -37.8)") transformed, err := g.Transform(func(ct geom.CoordinatesType, c []float64) error { return t.Forward(ct, c) }) if err != nil { log.Fatal(err) } fmt.Println(transformed.AsText()) // 3D transformation (EPSG:4979 = WGS84 3D, EPSG:4978 = WGS84 geocentric) t3d, err := proj.NewTransformation("EPSG:4979", "EPSG:4978") if err != nil { log.Fatal(err) } defer t3d.Release() coords3d := []float64{151.2093, -33.8688, 50.0} // lon, lat, height (m) if err := t3d.Forward(geom.DimXYZ, coords3d); err != nil { log.Fatal(err) } fmt.Printf("%.0f, %.0f, %.0f\n", coords3d[0], coords3d[1], coords3d[2]) ``` -------------------------------- ### Install proj dependency on macOS Source: https://github.com/peterstace/simplefeatures/blob/master/README.md Use Homebrew to install the proj dependency on macOS. ```bash brew install proj ``` -------------------------------- ### Polygon Definition Examples Source: https://github.com/peterstace/simplefeatures/blob/master/internal/cmprefimpl/testdata/strings.txt These examples demonstrate the syntax for defining various polygon shapes using coordinate pairs. They are useful for representing geographical areas or custom shapes in spatial data. ```text POLYGON((1 -1,6 4,4 6,-1 1,1 -1)) ``` ```text POLYGON((1 0,0 -1,-1 0,0 1,1 0)) ``` ```text POLYGON((1 0,0 0,0 1,0 4,1 4,1 5,3 5,3 0,1 0),(1 4,1 3,2 3,2 4,1 4),(1 2,1 1,2 1,2 2,1 2)) ``` ```text POLYGON((1 0,0 0,0 2,1 2,1 1,2 1,2 0,1 0)) ``` ```text POLYGON((1 0,0 1,0 0,1 0)) ``` ```text POLYGON((1 0,0 1,1 1,1 0)) ``` ```text POLYGON((1 0,0.9807852804032304 -0.19509032201612825,0.9238795325112867 -0.3826834323650898,0.8314696123025452 -0.5555702330196022,0.7071067811865476 -0.7071067811865475,0.5555702330196023 -0.8314696123025452,0.38268343236508984 -0.9238795325112867,0.19509032201612833 -0.9807852804032304,0.00000000000000006123233995736766 -1,-0.1950903220161282 -0.9807852804032304,-0.3826834323650897 -0.9238795325112867,-0.555570233019602 -0.8314696123025455,-0.7071067811865475 -0.7071067811865476,-0.8314696123025453 -0.5555702330196022,-0.9238795325112867 -0.38268343236509034,-0.9807852804032304 -0.1950903220161286,-1 -0.00000000000000012246467991473532,-0.9807852804032304 0.19509032201612836,-0.9238795325112868 0.38268343236508967,-0.8314696123025455 0.555570233019602,-0.7071067811865477 0.7071067811865475,-0.5555702330196022 0.8314696123025452,-0.38268343236509034 0.9238795325112865,-0.19509032201612866 0.9807852804032303,-0.00000000000000018369701987210297 1,0.1950903220161283 0.9807852804032304,0.38268343236509 0.9238795325112866,0.5555702330196018 0.8314696123025455,0.7071067811865474 0.7071067811865477,0.8314696123025452 0.5555702330196022,0.9238795325112865 0.3826834323650904,0.9807852804032303 0.19509032201612872,1 0)) ``` ```text POLYGON((1 0,0.9807852804032305 -0.19509032201612808,0.923879532511287 -0.3826834323650894,0.8314696123025456 -0.5555702330196017,0.7071067811865481 -0.7071067811865469,0.5555702330196031 -0.8314696123025447,0.38268343236509084 -0.9238795325112863,0.19509032201612964 -0.9807852804032302,0.0000000000000016155445744325867 -1,-0.19509032201612647 -0.9807852804032308,-0.38268343236508784 -0.9238795325112875,-0.5555702330196005 -0.8314696123025463,-0.7071067811865459 -0.7071067811865491,-0.8314696123025438 -0.5555702330196043,-0.9238795325112857 -0.38268343236509234,-0.9807852804032299 -0.19509032201613122,-1 -0.0000000000000032310891488651735,-0.9807852804032311 0.19509032201612486,-0.9238795325112882 0.38268343236508634,-0.8314696123025475 0.555570233019599,-0.7071067811865505 0.7071067811865446,-0.5555702330196058 0.8314696123025428,-0.3826834323650936 0.9238795325112852,-0.19509032201613213 0.9807852804032297,-0.000000000000003736410698672604 1,0.1950903220161248 0.9807852804032311,0.38268343236508673 0.9238795325112881,0.5555702330195996 0.8314696123025469,0.7071067811865455 0.7071067811865496,0.8314696123025438 0.5555702330196044,0.9238795325112859 0.38268343236509206,0.98078528040323 0.19509032201613047,1 0)) ``` -------------------------------- ### Serializing and Deserializing Geometries in Go Source: https://context7.com/peterstace/simplefeatures/llms.txt Shows how to marshal and unmarshal geometry types to and from WKT, WKB, GeoJSON, and TWKB formats. Includes integration examples for database/sql and options for skipping validation. ```go import ( "encoding/json" "fmt" "github.com/peterstace/simplefeatures/geom" ) // --- WKT --- g, err := geom.UnmarshalWKT("POLYGON((0 0,4 0,4 4,0 4,0 0))") if err != nil { log.Fatal(err) } fmt.Println(g.AsText()) // POLYGON((0 0,4 0,4 4,0 4,0 0)) // --- WKB --- wkb := g.AsBinary() g2, err := geom.UnmarshalWKB(wkb) if err != nil { log.Fatal(err) } fmt.Println(g2.AsText()) // --- GeoJSON --- gjson := `{"type":"Point","coordinates":[151.2093,-33.8688]}` g3, err := geom.UnmarshalGeoJSON([]byte(gjson)) if err != nil { log.Fatal(err) } buf, _ := json.Marshal(g3) fmt.Println(string(buf)) // {"type":"Point","coordinates":[151.2093,-33.8688]} // Unmarshal directly into concrete type var pt geom.Point if err := json.Unmarshal([]byte(gjson), &pt); err != nil { log.Fatal(err) } // --- TWKB (Tiny WKB, compact binary format) --- twkb, err := geom.MarshalTWKB(g, 6) // precXY=6 decimal places if err != nil { log.Fatal(err) } g4, err := geom.UnmarshalTWKB(twkb) if err != nil { log.Fatal(err) } fmt.Println(g4.AsText()) // --- database/sql integration --- // Geometry implements driver.Valuer and sql.Scanner via WKB: // db.QueryRow("SELECT geom FROM spatial_table WHERE id=$1", 1).Scan(&g) // db.Exec("INSERT INTO spatial_table(geom) VALUES ($1)", g) // --- Skip validation when unmarshaling untrusted/known-valid data --- g5, err := geom.UnmarshalWKB(wkb, geom.NoValidate{}) ``` -------------------------------- ### Java Interface with Default Method Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md Example of a Java interface defining methods, including a default implementation for `prevInRing`. ```java public interface SegmentString { int size(); Coordinate getCoordinate(int i); // Default implementation default Coordinate prevInRing(int index) { int prevIndex = index - 1; if (prevIndex < 0) prevIndex = size() - 2; return getCoordinate(prevIndex); } } ``` -------------------------------- ### GEOS Spatial Operations in Go Source: https://context7.com/peterstace/simplefeatures/llms.txt Utilizes the GEOS C library for advanced spatial predicates and operations. Requires libgeos-dev to be installed. ```go import ( "fmt" "github.com/peterstace/simplefeatures/geom" "github.com/peterstace/simplefeatures/geos" ) a, _ := geom.UnmarshalWKT("POLYGON((0 0,4 0,4 4,0 4,0 0))") b, _ := geom.UnmarshalWKT("POLYGON((2 2,6 2,6 6,2 6,2 2))") // All spatial predicates available via GEOS: ok, err := geos.Intersects(a, b) fmt.Println(ok, err) // true ok, _ = geos.Contains(a, b) fmt.Println(ok) // false mat, _ := geos.Relate(a, b) fmt.Println(mat) // "212101212" // Set operations via GEOS union, err := geos.Union(a, b) if err != nil { log.Fatal(err) } fmt.Println(union.AsText()) // Fix an invalid geometry invalid, _ := geom.UnmarshalWKT("POLYGON((0 0,4 0,2 2,4 4,0 4,2 2,0 0))", geom.NoValidate{}) valid, err := geos.MakeValid(invalid) if err != nil { log.Fatal(err) } fmt.Println(valid.Validate()) // // Concave hull (concavenessRatio 1.0 = convex, 0.0 = maximally concave) pts, _ := geom.UnmarshalWKT("MULTIPOINT((0 0),(4 0),(4 4),(0 4),(2 2))") hull, err := geos.ConcaveHull(pts, 0.5, false) if err != nil { log.Fatal(err) } fmt.Println(hull.AsText()) // Clip geometry to a rectangle defined by an Envelope rect := geom.NewEnvelope(geom.XY{X: 1, Y: 1}, geom.XY{X: 3, Y: 3}) clipped, err := geos.ClipByRect(a, rect) if err != nil { log.Fatal(err) } fmt.Println(clipped.AsText()) // POLYGON((1 1,3 1,3 3,1 3,1 1)) // Buffer via GEOS with options line, _ := geom.UnmarshalWKT("LINESTRING(0 0,10 0)") buf, err := geos.Buffer(line, 1.0, geos.BufferEndCapFlat(), geos.BufferJoinStyleBevel(), geos.BufferQuadSegments(4), ) if err != nil { log.Fatal(err) } fmt.Println(buf.AsText()) // Topology-preserving simplify via GEOS simplified, err := geos.TopologyPreserveSimplify(a, 0.5) if err != nil { log.Fatal(err) } fmt.Println(simplified.AsText()) ``` -------------------------------- ### Go Constructor Chaining Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md Demonstrates how to implement multiple constructors in Go using descriptive function names and chaining, mimicking Java's constructor overloading. ```Go // Default constructor func NewAccount() *Account { return NewAccountWithBalance(0.0) } // Constructor with balance (chains to full constructor) func NewAccountWithBalance(balance float64) *Account { return NewAccountWithBalanceAndCurrency(balance, "USD") } // Full constructor func NewAccountWithBalanceAndCurrency(balance float64, currency string) *Account { return &Account{balance: balance, currency: currency} } ``` -------------------------------- ### Go IIFE for Static Initialization Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md Shows how to initialize complex static fields in Go using Immediately Invoked Function Expressions (IIFEs) when direct assignment is not feasible. ```Go var Util_Counter_default = func() *Util_Counter { return Util_NewCounter() }() ``` -------------------------------- ### Create and Query Envelopes Source: https://context7.com/peterstace/simplefeatures/llms.txt Shows how to construct envelopes from points, extract them from geometries, and perform spatial checks. Useful for bounding box calculations and spatial queries. ```go import "github.com/peterstace/simplefeatures/geom" // Build from XY points env := geom.NewEnvelope( geom.XY{X: 0, Y: 0}, geom.XY{X: 4, Y: 4}, ) fmt.Println(env.IsRectangle()) // true fmt.Println(env.Width()) // 4 fmt.Println(env.Height()) // 4 fmt.Println(env.Area()) // 16 // Extract from geometry poly, _ := geom.UnmarshalWKT("POLYGON((1 2,5 2,5 7,1 2))") e := poly.Envelope() lo, hi, ok := e.MinMaxXYs() fmt.Println(ok, lo, hi) // true {1 2} {5 7} // Spatial checks on envelopes e2 := geom.NewEnvelope(geom.XY{X: 3, Y: 3}, geom.XY{X: 8, Y: 8}) fmt.Println(e.Intersects(e2)) // true fmt.Println(e.Contains(e2)) // false // Expand envelope to include a point expanded := e.ExpandToIncludeXY(geom.XY{X: 10, Y: 10}) fmt.Println(expanded.Width()) // 9 // Distance between two envelopes dist, ok := e.Distance(e2) fmt.Println(dist, ok) // 0 true (they overlap) // Convert to geometry for use in spatial ops geomEnv := e.AsGeometry() fmt.Println(geomEnv.AsText()) // POLYGON((1 2,5 2,5 7,1 2)) // Convert to rtree.Box for use with the rtree package box, ok := e.AsBox() fmt.Println(ok, box.MinX, box.MinY, box.MaxX, box.MaxY) ``` -------------------------------- ### Constructing Geometry Types in Go Source: https://context7.com/peterstace/simplefeatures/llms.txt Demonstrates how to create various OGC geometry types using their respective constructors. Supports 2D, 3D, and empty geometries. Includes wrapping concrete types in the generic Geometry type and safely extracting them. ```go import "github.com/peterstace/simplefeatures/geom" // Point — using typed constructors pt := geom.NewPointXY(151.2093, -33.8688) pt3d := geom.NewPointXYZ(151.2093, -33.8688, 10.5) // Empty point emptyPt := geom.NewEmptyPoint(geom.DimXY) // LineString — built from a Sequence seq := geom.NewSequence([]float64{0, 0, 1, 1, 2, 0}, geom.DimXY) ls := geom.NewLineString(seq) fmt.Println(ls.AsText()) // LINESTRING(0 0,1 1,2 0) // Polygon — exterior ring + optional holes ring := geom.NewSequence([]float64{0,0, 4,0, 4,4, 0,4, 0,0}, geom.DimXY) poly := geom.NewPolygon([]geom.LineString{geom.NewLineString(ring)}) if err := poly.Validate(); err != nil { log.Fatal(err) } fmt.Println(poly.AsText()) // POLYGON((0 0,4 0,4 4,0 4,0 0)) // Wrap any concrete type in the generic Geometry g := poly.AsGeometry() fmt.Println(g.Type()) // Polygon fmt.Println(g.Dimension()) // 2 // Extract concrete type safely if p, ok := g.AsPolygon(); ok { fmt.Println(p.Area()) // 16 } // Or panic-style if type is known p := g.MustAsPolygon() // MultiPoint from coordinates mp := geom.NewMultiPointXY(0, 0, 1, 1, 2, 2) fmt.Println(mp.AsText()) // MULTIPOINT((0 0),(1 1),(2 2)) ``` -------------------------------- ### Get Reason for Invalid Geometry from GeoJSON Source: https://github.com/peterstace/simplefeatures/blob/master/internal/cmprefimpl/testdata/strings.txt Returns a detailed reason why a geometry in GeoJSON format is considered invalid. ```sql SELECT ST_IsValidReason(ST_GeomFromGeoJSON($1)) ``` -------------------------------- ### Build R-Tree with BulkLoad Source: https://context7.com/peterstace/simplefeatures/llms.txt Efficiently builds an in-memory R-tree from a static set of items using their bounding boxes. Use for static spatial indexing where performance is critical. ```go import ( "fmt" "github.com/peterstace/simplefeatures/rtree" ) // Index a set of rectangles by their bounding boxes items := []rtree.BulkItem{ {Box: rtree.Box{MinX: 0, MinY: 0, MaxX: 2, MaxY: 2}, RecordID: 0}, {Box: rtree.Box{MinX: 1, MinY: 1, MaxX: 3, MaxY: 3}, RecordID: 1}, {Box: rtree.Box{MinX: 5, MinY: 5, MaxX: 7, MaxY: 7}, RecordID: 2}, {Box: rtree.Box{MinX: 8, MinY: 8, MaxX: 10, MaxY: 10}, RecordID: 3}, } tree := rtree.BulkLoad(items) fmt.Println(tree.Count()) // 4 // Search for items overlapping the query box query := rtree.Box{MinX: 0.5, MinY: 0.5, MaxX: 2.5, MaxY: 2.5} var found []int err := tree.RangeSearch(query, func(id int) error { found = append(found, id) return nil }) if err != nil { log.Fatal(err) } fmt.Println(found) // [0 1] (records 0 and 1 overlap the query) // Stop search early using the sentinel Stop error var first int = -1 tree.RangeSearch(query, func(id int) error { first = id return rtree.Stop // stops iteration without error }) fmt.Println(first) // 0 (or 1, order is not guaranteed) // Extent of the whole tree extent, ok := tree.Extent() fmt.Println(ok, extent.MinX, extent.MinY, extent.MaxX, extent.MaxY) // true 0 0 10 10 // Find nearest entry to a given box (priority search) nearest := rtree.Box{MinX: 4, MinY: 4, MaxX: 4, MaxY: 4} var nearestID int tree.PrioritySearch(nearest, func(id int) error { nearestID = id return rtree.Stop }) fmt.Println(nearestID) // 1 ``` -------------------------------- ### Interpolate Point on LineString Source: https://context7.com/peterstace/simplefeatures/llms.txt The InterpolatePoint method calculates a point along a LineString at a specified fractional distance from its start. The distance is normalized to the total length of the LineString. ```go import "github.com/peterstace/simplefeatures/geom" ls := geom.NewLineString(geom.NewSequence( []float64{0, 0, 10, 0, 10, 10}, geom.DimXY, )) // Interpolate a single point at a fraction of the total length pt := ls.InterpolatePoint(0.5) xy, _ := pt.XY() fmt.Printf("%.1f, %.1f\n", xy.X, xy.Y) // 10.0, 0.0 (midpoint of 20-unit line) ``` -------------------------------- ### Build and Manipulate 3D Sequences Source: https://context7.com/peterstace/simplefeatures/llms.txt Demonstrates creating a 3D sequence, accessing its properties, and changing its coordinate type. Use when dealing with ordered point data in 2D, 3D, or higher dimensions. ```go import "github.com/peterstace/simplefeatures/geom" // Build a 3D XYZ sequence coords := []float64{ 0, 0, 10, // point 0 1, 0, 20, // point 1 1, 1, 30, // point 2 } seq := geom.NewSequence(coords, geom.DimXYZ) fmt.Println(seq.Length()) // 3 fmt.Println(seq.CoordinatesType()) // XYZ c := seq.Get(1) fmt.Printf("X=%.0f Y=%.0f Z=%.0f\n", c.X, c.Y, c.Z) // X=1 Y=0 Z=20 // Force coordinate type change (drops Z for XY) ls := geom.NewLineString(seq) ls2D := ls.ForceCoordinatesType(geom.DimXY) fmt.Println(ls2D.Coordinates().CoordinatesType()) // XY // Force2D convenience g, _ := geom.UnmarshalWKT("POINT Z(1 2 3)") flat := g.Force2D() fmt.Println(flat.AsText()) // POINT(1 2) ``` -------------------------------- ### Get Reason for Invalid Geometry from Hexadecimal WKB Source: https://github.com/peterstace/simplefeatures/blob/master/internal/cmprefimpl/testdata/strings.txt Retrieves the reason for a geometry's invalidity when provided as a hexadecimal WKB string. The input is decoded from hex before validation. ```sql SELECT ST_IsValidReason(ST_GeomFromWKB(decode($1, 'hex'))) ``` -------------------------------- ### Go Runtime Type Checking with java.InstanceOf Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md Demonstrates the correct way to perform Java-style 'instanceof' checks in Go using `java.InstanceOf` to handle inheritance hierarchies, contrasting it with incorrect direct type assertions. ```go // CORRECT: Java instanceof → java.InstanceOf // Java: if (obj instanceof GeometryCollection) if java.InstanceOf[*Geom_GeometryCollection](obj) { ... } // WRONG: Direct type assertion fails for subtypes // This only matches *Geom_GeometryCollection exactly, NOT MultiPolygon if _, ok := obj.GetSelf().(*Geom_GeometryCollection); ok { ... } // DON'T DO THIS // InstanceOf returns true for parent types too: java.InstanceOf[*PaymentMethod](creditCard) // true java.InstanceOf[*CardPayment](creditCard) // true java.InstanceOf[*CreditCard](creditCard) // true ``` -------------------------------- ### Go Implementation of Java Default Method Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md When Go types don't override default Java interface methods, copy the default body into the Go implementation. This example shows the `PrevInRing` method. ```go type Noding_SegmentString interface { IsNoding_SegmentString() Size() int GetCoordinate(i int) *Geom_Coordinate PrevInRing(index int) *Geom_Coordinate } // Each implementation includes the default behavior: func (ss *Noding_BasicSegmentString) PrevInRing(index int) *Geom_Coordinate { prevIndex := index - 1 if prevIndex < 0 { prevIndex = ss.Size() - 2 } return ss.GetCoordinate(prevIndex) } ``` -------------------------------- ### Execute Spatial Predicates and DE-9IM Computations Source: https://context7.com/peterstace/simplefeatures/llms.txt Applies various spatial predicates like Intersects, Contains, Within, Covers, and Disjoint. Also demonstrates raw DE-9IM matrix computation and pattern matching using RelateMatches. ```go import "github.com/peterstace/simplefeatures/geom" poly, _ := geom.UnmarshalWKT("POLYGON((0 0,4 0,4 4,0 4,0 0))") pt, _ := geom.UnmarshalWKT("POINT(2 2)") outer, _ := geom.UnmarshalWKT("POLYGON((0 0,10 0,10 10,0 10,0 0))") // Intersects (no error return — uses fast implementation) fmt.Println(geom.Intersects(poly, pt)) // true // Predicate functions return (bool, error) ok, err := geom.Contains(poly, pt) fmt.Println(ok, err) // true ok, _ = geom.Within(pt, poly) fmt.Println(ok) // true ok, _ = geom.Covers(poly, pt) fmt.Println(ok) // true ok, _ = geom.CoveredBy(pt, poly) fmt.Println(ok) // true ok, _ = geom.Disjoint(poly, outer) fmt.Println(ok) // false a, _ := geom.UnmarshalWKT("LINESTRING(0 0,2 2)") b, _ := geom.UnmarshalWKT("LINESTRING(0 2,2 0)") ok, _ = geom.Crosses(a, b) fmt.Println(ok) // true // Raw DE-9IM matrix mat, err := geom.Relate(poly, pt) if err != nil { log.Fatal(err) } fmt.Println(mat) // e.g., "0F2FF1FF2" // Pattern matching against a DE-9IM matrix match, err := geom.RelateMatches(mat, "T*F**F***") fmt.Println(match) // true (point is in polygon interior) ok, _ = geom.Equals(poly, poly) fmt.Println(ok) // true ok, _ = geom.Overlaps( mustWKT("POLYGON((0 0,3 0,3 3,0 3,0 0))"), mustWKT("POLYGON((1 1,4 1,4 4,1 4,1 1))"), ) fmt.Println(ok) // true ``` -------------------------------- ### Open Java and Go Files Side-by-Side in Vim Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/REVIEW_APPROACH.md Use this command to open Java and Go files side-by-side in Vim for manual comparison during the review process. The LLM reviewer should provide this command for each file pair. ```bash vim -O ``` -------------------------------- ### Java Class to Go Struct Mapping Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md Demonstrates the direct mapping of a Java class with static and instance members to a Go struct and associated functions, preserving element order. ```java public class Foo { public static final double PI = 3.14; private static Bar createBar() { ... } public static Foo valueOf(String s) { ... } private static final double EPSILON = 0.001; private double value; public Foo() { ... } public double getValue() { ... } public void setValue(double v) { ... } } ``` ```go var Foo_PI = 3.14 func foo_createBar() *Bar { ... } func Foo_ValueOfString(s string) *Foo { ... } const foo_epsilon = 0.001 type Foo struct { value float64 } func NewFoo() *Foo { ... } func (f *Foo) GetValue() float64 { ... } func (f *Foo) SetValue(v float64) { ... } ``` -------------------------------- ### Go Initialization Order Exception Note Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md Illustrates how to add a transliteration note in Go when reordering declarations is necessary due to initialization order constraints, referencing a value declared later in the original Java source. ```go // TRANSLITERATION NOTE: Moved above foo_epsilon due to Go initialization order // requirements - foo_epsilon references this value. var foo_baseValue = 1.0 ``` -------------------------------- ### Simplify, Densify, SnapToGrid, Reverse, ForceCW/CCW, TransformXY, FlipCoordinates Source: https://context7.com/peterstace/simplefeatures/llms.txt Provides methods for simplifying geometries using the Ramer-Douglas-Peucker algorithm, densifying linestrings by adding points, snapping coordinates to a grid, reversing coordinate order, enforcing clockwise or counter-clockwise winding, applying arbitrary coordinate transformations, and flipping X/Y coordinates. ```APIDOC ## Simplify ### Description Simplifies a geometry using the Ramer-Douglas-Peucker algorithm with a specified tolerance. ### Method `Simplify(tolerance float64)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go // For LineString complex, _ := geom.UnmarshalWKT("LINESTRING(0 0,0.1 0.1,1 0,2 0.1,3 0)") simplified, err := geom.NewLineString(geom.NewSequence([]float64{0,0,0.1,0.1,1,0,2,0.1,3,0}, geom.DimXY)).Simplify(0.2) // For Geometry interface g, _ := geom.UnmarshalWKT("POLYGON((0 0,4 0,4 0.1,4 4,0 4,0 0))") sg, err := g.Simplify(0.5) ``` ### Response #### Success Response (Geometry) - Returns the simplified geometry. #### Response Example ```go // LINESTRING(0 0,3 0) // POLYGON((0 0,4 0,4 0.1,4 4,0 4,0 0)) ``` ## Densify ### Description Adds interpolated points to a linestring such that the maximum segment length does not exceed `maxDist`. ### Method `Densify(maxDist float64)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go sparse, _ := geom.UnmarshalWKT("LINESTRING(0 0,10 0)") dense := sparse.Densify(2.5) ``` ### Response #### Success Response (LineString) - Returns the densified linestring. #### Response Example ```go // LINESTRING(0 0,2.5 0,5 0,7.5 0,10 0) ``` ## SnapToGrid ### Description Snaps the coordinates of a geometry to a specified number of decimal places. ### Method `SnapToGrid(decimalPlaces int)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go noisy, _ := geom.UnmarshalWKT("POINT(1.234567 2.345678)") snapped := noisy.SnapToGrid(2) ``` ### Response #### Success Response (Geometry) - Returns the geometry with snapped coordinates. #### Response Example ```go // POINT(1.23 2.35) ``` ## Reverse ### Description Reverses the order of coordinates in a linestring or polygon ring. ### Method `Reverse()` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go ls, _ := geom.UnmarshalWKT("LINESTRING(0 0,1 1,2 2)") fmt.Println(ls.Reverse().AsText()) ``` ### Response #### Success Response (Geometry) - Returns the geometry with reversed coordinates. #### Response Example ```go // LINESTRING(2 2,1 1,0 0) ``` ## ForceCW / ForceCCW ### Description Forces the winding order of a polygon to be clockwise (ForceCW) or counter-clockwise (ForceCCW). ### Method `ForceCW()` or `ForceCCW()` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go poly, _ := geom.UnmarshalWKT("POLYGON((0 0,0 4,4 4,4 0,0 0))") // CCW cw := poly.ForceCW() ``` ### Response #### Success Response (Polygon) - Returns the polygon with the enforced winding order. #### Response Example ```go // POLYGON((0 0,4 0,4 4,0 4,0 0)) ``` ## TransformXY ### Description Applies an arbitrary coordinate transformation function to each point of a geometry. ### Method `TransformXY(transformFunc func(geom.XY) geom.XY)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go poly, _ := geom.UnmarshalWKT("POLYGON((0 0,0 4,4 4,4 0,0 0))") rotated := poly.TransformXY(func(xy geom.XY) geom.XY { cos45 := 0.7071 return geom.XY{ X: xy.X*cos45 - xy.Y*cos45, Y: xy.X*cos45 + xy.Y*cos45, } }) ``` ### Response #### Success Response (Geometry) - Returns the transformed geometry. ## FlipCoordinates ### Description Swaps the X and Y coordinates of a geometry. Useful for converting between lat/lon and lon/lat. ### Method `FlipCoordinates()` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go pt := geom.NewPointXY(51.5, -0.12) // lat, lon flipped := pt.FlipCoordinates() ``` ### Response #### Success Response (Geometry) - Returns the geometry with flipped coordinates. #### Response Example ```go // If pt was POINT(51.5 -0.12), flipped would represent POINT(-0.12 51.5) ``` ``` -------------------------------- ### Efficient Repeated Predicate Evaluation with PreparedGeometry Source: https://context7.com/peterstace/simplefeatures/llms.txt Preprocess a geometry once to cache spatial indices for fast repeated evaluation of spatial predicates against many other geometries. Ensure error handling for the Prepare function. ```go import "github.com/peterstace/simplefeatures/geom" country, _ := geom.UnmarshalWKT("POLYGON((0 0,100 0,100 100,0 100,0 0))") // Prepare once — caches spatial indices prepared, err := geom.Prepare(country) if err != nil { log.Fatal(err) } // Test many points efficiently cities := []geom.Geometry{ geom.NewPointXY(10, 10).AsGeometry(), geom.NewPointXY(50, 50).AsGeometry(), geom.NewPointXY(200, 200).AsGeometry(), } for _, city := range cities { ok, err := prepared.Contains(city) if err != nil { log.Fatal(err) } fmt.Printf("%s inside: %v\n", city.AsText(), ok) } // POINT(10 10) inside: true // POINT(50 50) inside: true // POINT(200 200) inside: false // Other available predicates: // prepared.Intersects(g) // prepared.ContainsProperly(g) // prepared.CoveredBy(g) // prepared.Covers(g) // prepared.Disjoint(g) // prepared.Overlaps(g) // prepared.Touches(g) // prepared.Within(g) ``` -------------------------------- ### Perform Overlay Operations on Geometries Source: https://context7.com/peterstace/simplefeatures/llms.txt Utilizes JTS port for robust overlay operations including Union, Intersection, Difference, and SymmetricDifference between two geometries. Also shows how to union a collection of geometries and perform unary union. ```go import "github.com/peterstace/simplefeatures/geom" a, _ := geom.UnmarshalWKT("POLYGON((0 0,4 0,4 4,0 4,0 0))") b, _ := geom.UnmarshalWKT("POLYGON((2 2,6 2,6 6,2 6,2 2))") union, err := geom.Union(a, b) if err != nil { log.Fatal(err) } fmt.Println(union.AsText()) // POLYGON((0 0,4 0,4 2,6 2,6 6,2 6,2 4,0 4,0 0)) intersection, err := geom.Intersection(a, b) if err != nil { log.Fatal(err) } fmt.Println(intersection.AsText()) // POLYGON((2 2,4 2,4 4,2 4,2 2)) diff, err := geom.Difference(a, b) if err != nil { log.Fatal(err) } fmt.Println(diff.AsText()) symDiff, err := geom.SymmetricDifference(a, b) if err != nil { log.Fatal(err) } fmt.Println(symDiff.AsText()) // Union a collection of geometries into one lines := []geom.Geometry{ mustWKT("LINESTRING(0 0,1 1)"), mustWKT("LINESTRING(1 1,2 2)"), mustWKT("LINESTRING(2 2,3 3)"), } merged, err := geom.UnionMany(lines) if err != nil { log.Fatal(err) } fmt.Println(merged.AsText()) // LINESTRING(0 0,1 1,2 2,3 3) // Unary union — dissolve the components of a single geometry gc, _ := geom.UnmarshalWKT("GEOMETRYCOLLECTION(POLYGON((0 0,2 0,2 2,0 2,0 0)),POLYGON((1 1,3 1,3 3,1 3,1 1)))") result, err := geom.UnaryUnion(gc) if err != nil { log.Fatal(err) } fmt.Println(result.AsText()) ``` -------------------------------- ### Java instanceof to Go using java.InstanceOf Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md To replicate Java's `instanceof` checks in Go, use `java.InstanceOf[T](obj)`. This should be paired with `java.Cast[T](obj)` for type assertions. ```go // Java: // if (geom instanceof Polygon) { // Polygon poly = (Polygon) geom; // // use poly // } // Go: if java.InstanceOf[*Geom_Polygon](geom) { poly := java.Cast[*Geom_Polygon](geom) // use poly } ``` -------------------------------- ### Go Stub Implementation for Unported Classes Source: https://github.com/peterstace/simplefeatures/blob/master/internal/jtsport/TRANSLITERATION_GUIDE.md When porting code that depends on unported classes, create stub implementations in a dedicated `stubs.go` file. These stubs include a panic to indicate they are not yet implemented. ```go package jts // ============================================================================= // STUBS: Stub types for classes not yet ported. Will be replaced when ported. // ============================================================================= // STUB: Geom_GeometryFactory - will be ported in Phase 2f. type Geom_GeometryFactory struct{} // STUB: Method stubbed - Geom_GeometryFactory not yet ported. func (gf *Geom_GeometryFactory) GetSRID() int { panic("Geom_GeometryFactory not yet ported") } ``` -------------------------------- ### Generate Geometry Summary Source: https://context7.com/peterstace/simplefeatures/llms.txt The Summary method provides a concise, human-readable description of a geometry, similar to PostGIS's ST_Summary function. It includes the geometry type, dimension, and key characteristics like the number of rings or points. ```go import "github.com/peterstace/simplefeatures/geom" // Summary (PostGIS ST_Summary-style) fmt.Println(poly2.Summary()) // Polygon[XY] with 1 ring consisting of 4 points ``` -------------------------------- ### PreparedGeometry - Efficient Repeated Predicate Evaluation Source: https://context7.com/peterstace/simplefeatures/llms.txt Preprocess a geometry once to cache spatial indices for fast repeated evaluation of spatial predicates against many other geometries. This includes methods like Contains, Intersects, Covers, etc. ```APIDOC ## Prepare Geometry ### Description Preprocesses a geometry to cache spatial indices for efficient repeated spatial predicate evaluations. ### Function Signature `geom.Prepare(g Geometry) (*PreparedGeometry, error)` ### Usage Example ```go country, _ := geom.UnmarshalWKT("POLYGON((0 0,100 0,100 100,0 100,0 0))") prepared, err := geom.Prepare(country) // ... handle error ... city := geom.NewPointXY(10, 10).AsGeometry() ok, err := prepared.Contains(city) // ... handle error ... ``` ### Available Predicates - `prepared.Contains(g Geometry) (bool, error)` - `prepared.Intersects(g Geometry) (bool, error)` - `prepared.ContainsProperly(g Geometry) (bool, error)` - `prepared.CoveredBy(g Geometry) (bool, error)` - `prepared.Covers(g Geometry) (bool, error)` - `prepared.Disjoint(g Geometry) (bool, error)` - `prepared.Overlaps(g Geometry) (bool, error)` - `prepared.Touches(g Geometry) (bool, error)` - `prepared.Within(g Geometry) (bool, error)` ```