From 98baff10f45bc772275568860c4a7965e836a8de Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 02:55:34 +0000 Subject: [PATCH] fix(deps): update module github.com/twpayne/go-geos to v0.20.2 --- go.mod | 4 +- go.sum | 2 + .../github.com/twpayne/go-geos/.golangci.yml | 194 +++-- .../twpayne/go-geos/CONTRIBUTING.md | 13 +- vendor/github.com/twpayne/go-geos/README.md | 52 +- .../twpayne/go-geos/{bounds.go => box2d.go} | 40 +- vendor/github.com/twpayne/go-geos/box3d.go | 44 ++ .../github.com/twpayne/go-geos/bufparams.go | 87 +++ vendor/github.com/twpayne/go-geos/context.go | 495 +++---------- vendor/github.com/twpayne/go-geos/coordseq.go | 188 +++-- .../twpayne/go-geos/defaultcontext.go | 13 +- .../twpayne/go-geos/geojsonreader.go | 47 ++ .../twpayne/go-geos/geojsonwriter.go | 46 ++ vendor/github.com/twpayne/go-geos/geom.go | 442 +++++++---- .../github.com/twpayne/go-geos/geommethods.go | 684 +++++++----------- .../twpayne/go-geos/geommethods.go.tmpl | 69 +- .../twpayne/go-geos/geommethods.yaml | 19 +- vendor/github.com/twpayne/go-geos/geos.go | 42 +- vendor/github.com/twpayne/go-geos/go-geos.c | 48 +- vendor/github.com/twpayne/go-geos/go-geos.h | 26 +- vendor/github.com/twpayne/go-geos/prepgeom.go | 227 +++--- vendor/github.com/twpayne/go-geos/strtree.go | 196 ++--- .../github.com/twpayne/go-geos/wkbreader.go | 46 ++ .../github.com/twpayne/go-geos/wkbwriter.go | 75 ++ .../github.com/twpayne/go-geos/wktreader.go | 47 ++ .../github.com/twpayne/go-geos/wktwriter.go | 45 ++ vendor/modules.txt | 4 +- 27 files changed, 1850 insertions(+), 1345 deletions(-) rename vendor/github.com/twpayne/go-geos/{bounds.go => box2d.go} (64%) create mode 100644 vendor/github.com/twpayne/go-geos/box3d.go create mode 100644 vendor/github.com/twpayne/go-geos/bufparams.go create mode 100644 vendor/github.com/twpayne/go-geos/geojsonreader.go create mode 100644 vendor/github.com/twpayne/go-geos/geojsonwriter.go create mode 100644 vendor/github.com/twpayne/go-geos/wkbreader.go create mode 100644 vendor/github.com/twpayne/go-geos/wkbwriter.go create mode 100644 vendor/github.com/twpayne/go-geos/wktreader.go create mode 100644 vendor/github.com/twpayne/go-geos/wktwriter.go diff --git a/go.mod b/go.mod index fb8b8824..9dc8afa3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/dechristopher/lod -go 1.19 +go 1.24.0 require ( github.com/BurntSushi/toml v1.3.2 @@ -11,7 +11,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_model v0.3.0 - github.com/twpayne/go-geos v0.13.2 + github.com/twpayne/go-geos v0.20.2 github.com/valyala/fasthttp v1.45.0 golang.org/x/sync v0.2.0 ) diff --git a/go.sum b/go.sum index 87a4b738..c062f7f2 100644 --- a/go.sum +++ b/go.sum @@ -70,6 +70,8 @@ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/twpayne/go-geos v0.13.2 h1:kyQenUwpU+mGrNX403KNlRDuIIIL22OHVVj1SGfXUm0= github.com/twpayne/go-geos v0.13.2/go.mod h1:r5O89NwzDqYqiDF5HnkYjdgJtODwzpjeNlj/gL9ztXk= +github.com/twpayne/go-geos v0.20.2 h1:Ll3j/lpjHOX0b0ZNKG78+THoLTmDU5Yt2W25VB2gFKg= +github.com/twpayne/go-geos v0.20.2/go.mod h1:lQaCKumxP0RKfz9bF78GLGC8hpKi+sWbDovX0Pe57RM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA= diff --git a/vendor/github.com/twpayne/go-geos/.golangci.yml b/vendor/github.com/twpayne/go-geos/.golangci.yml index 6f1cb0e7..ebf7b374 100644 --- a/vendor/github.com/twpayne/go-geos/.golangci.yml +++ b/vendor/github.com/twpayne/go-geos/.golangci.yml @@ -1,109 +1,211 @@ +version: '2' +run: + go: '1.24' linters: enable: - - asasalint - asciicheck - bidichk - bodyclose + - canonicalheader - containedctx - - contextcheck + - copyloopvar - decorder - - depguard - dogsled - dupword - durationcheck - - errcheck + - err113 - errchkjson - errname - errorlint - - execinquery - - exhaustive - - exportloopref + - exptostd + - fatcontext - forbidigo - forcetypeassert - - gci + - funcorder - gocheckcompilerdirectives - - gochecknoinits - - goconst + - gochecksumtype - gocritic - godot - - goerr113 - - gofmt - - gofumpt - - goimports - gomodguard - goprintffuncname - - gosec - - gosimple - - govet + - gosmopolitan - grouper + - iface - importas - - ineffassign + - inamedparam - interfacebloat - - ireturn + - intrange - loggercheck + - makezero + - mirror - misspell - - nakedret - nilerr + - nilnesserr - noctx - nolintlint - nosprintfhostport + - perfsprint - prealloc - predeclared - promlinter + - protogetter - reassign + - revive + - rowserrcheck + - sloglint + - spancheck + - sqlclosecheck - staticcheck - - stylecheck + - tagalign - tagliatelle - - tenv - testableexamples - - testpackage + - testifylint - thelper - - typecheck - unconvert - unparam - - unused - usestdlibvars + - usetesting + - wastedassign - whitespace + - zerologlint disable: + - arangolint + - asasalint + - contextcheck - cyclop + - depguard - dupl + - exhaustive - exhaustruct - funlen - ginkgolinter - gochecknoglobals + - gochecknoinits - gocognit + - goconst - gocyclo - godox - goheader - - gomnd - gomoddirectives + - gosec + - ireturn - lll - maintidx - - makezero + - musttag + - nakedret - nestif + - nilnil - nlreturn - nonamedreturns - paralleltest - - revive - - rowserrcheck # https://github.com/golangci/golangci-lint/issues/2649 - - sqlclosecheck # https://github.com/golangci/golangci-lint/issues/2649 + - recvcheck + - testpackage - tparallel - varnamelen - - wastedassign # https://github.com/golangci/golangci-lint/issues/2649 - wrapcheck - wsl - -linters-settings: - gci: - sections: - - standard - - default - - prefix(github.com/twpayne/go-geos) - goimports: - local-prefixes: github.com/twpayne/go-geos - -issues: - exclude-rules: - - linters: - - goerr113 - text: "do not define dynamic errors, use wrapped static errors instead" + - wsl_v5 + settings: + forbidigo: + exclude-godoc-examples: true + analyze-types: true + gocritic: + enable-all: true + disabled-checks: + - dupImport + - emptyFallthrough + - hugeParam + - rangeValCopy + - unnamedResult + - whyNoLint + govet: + disable: + - fieldalignment + - shadow + enable-all: true + misspell: + locale: US + ignore-rules: + - mitre + revive: + enable-all-rules: true + rules: + - name: add-constant + disabled: true + - name: call-to-gc + disabled: true + - name: cognitive-complexity + disabled: true + - name: cyclomatic + disabled: true + - name: empty-block + disabled: true + - name: exported + disabled: true + - name: filename-format + arguments: + - ^[a-z][-0-9_a-z]*(?:\.gen)?\.go$ + - name: flag-parameter + disabled: true + - name: function-length + disabled: true + - name: function-result-limit + disabled: true + - name: import-shadowing + disabled: true + - name: line-length-limit + disabled: true + - name: max-control-nesting + disabled: true + - name: max-public-structs + disabled: true + - name: nested-structs + disabled: true + - name: unused-parameter + disabled: true + - name: unused-receiver + disabled: true + staticcheck: + checks: + - all + exclusions: + generated: lax + presets: + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - err113 + text: do not define dynamic errors, use wrapped static errors instead + - linters: + - forbidigo + path: ^internal/cmds/ + - linters: + - forcetypeassert + path: _test\.go$ + - linters: + - forbidigo + path: assets/scripts/generate-commit.go +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + - golines + settings: + gci: + sections: + - standard + - default + - prefix(github.com/twpayne/go-geos) + gofumpt: + module-path: github.com/twpayne/go-geos + extra-rules: true + goimports: + local-prefixes: + - github.com/twpayne/go-geos + golines: + max-len: 256 + tab-len: 4 diff --git a/vendor/github.com/twpayne/go-geos/CONTRIBUTING.md b/vendor/github.com/twpayne/go-geos/CONTRIBUTING.md index e5c91e13..5f51a6c0 100644 --- a/vendor/github.com/twpayne/go-geos/CONTRIBUTING.md +++ b/vendor/github.com/twpayne/go-geos/CONTRIBUTING.md @@ -53,6 +53,8 @@ miss newly-added features. In these cases: [`go-geos.c`](https://github.com/twpayne/go-geos/blob/master/go-geos.c) and [`go-geos.h`](https://github.com/twpayne/go-geos/blob/master/go-geos.h) to provide the function when it is not provided. +* [`VersionCompare`](https://pkg.go.dev/github.com/twpayne/go-geos#VersionCompare) + can be used in tests for the CI to pass on all versions. ## C code formatting @@ -61,4 +63,13 @@ format C code. You can run this with: ```console $ clang-format -i *.c *.h -``` \ No newline at end of file +``` + +## Go code linting + +`go-geos` uses [`golangci-lint`](https://golangci-lint.run/) to lint Go code. +You can run it with: + +``` +$ golangci-lint run +``` diff --git a/vendor/github.com/twpayne/go-geos/README.md b/vendor/github.com/twpayne/go-geos/README.md index 019a448d..811367a1 100644 --- a/vendor/github.com/twpayne/go-geos/README.md +++ b/vendor/github.com/twpayne/go-geos/README.md @@ -4,6 +4,16 @@ Package `go-geos` provides an interface to [GEOS](https://libgeos.org). +## Install + +```console +$ go get github.com/twpayne/go-geos +``` + +You must also install the GEOS development headers and libraries. These are +typically in the package `libgeos-dev` on Debian-like systems, `geos-devel` on +RedHat-like systems, and `geos` in Homebrew. + ## Features * Fluent Go API. @@ -33,27 +43,22 @@ Package `go-geos` provides an interface to [GEOS](https://libgeos.org). * Optimized GeoJSON encoder. -* Automatic finalization of GEOS objects. +* Automatic cleanup of GEOS objects. ## Memory management -`go-geos` objects live mostly on the C heap. `go-geos` sets finalizers on the -objects it creates that free the associated C memory. However, the C heap is not -visible to the Go runtime. The can result in significant memory pressure as -memory is consumed by large, non-finalized geometries, of which the Go runtime -is unaware. Consequently, if it is known that a geometry will no longer be used, -it should be explicitly freed by calling its `Destroy()` method. Periodic calls -to `runtime.GC()` can also help, but the Go runtime makes no guarantees about -when or if finalizers will be called. +`go-geos` objects live mostly on the C heap. `go-geos` sets cleanup functions on +the objects it creates that free the associated C memory. However, the C heap is +not visible to the Go runtime. The can result in significant memory pressure as +memory is consumed by large, un-freed geometries, of which the Go runtime is +unaware. -You can set a function to be called whenever a geometry's finalizer is invoked -with the `WithGeomFinalizeFunc` option to `NewContext()`. This can be helpful -for tracking down geometry leaks. +## Ownership -For more information, see the [documentation for -`runtime.SetFinalizer()`](https://pkg.go.dev/runtime#SetFinalizer) and [this -thread on -`golang-nuts`](https://groups.google.com/g/golang-nuts/c/XnV16PxXBfA/m/W8VEzIvHBAAJ). +Returned sub-geometries (e.g. polygon rings or geometries in a collection) and +coordinate sequences are owned by the geometry and are only valid for as long as +the original geometry exists. If you need to persist a sub-geometry for longer +than the original geometry you must clone it. ## Errors, exceptions, and panics @@ -85,6 +90,19 @@ by GEOS and are willing to handle memory management manually. `go-geom` is recommended for long-running processes with less stringent geometry function requirements. +## GEOS version compatibility + +`go-geos` is tested to work with the versions of `GEOS` tested on CI. +See [here](.github/workflows/main.yml). + +Calling functions unsupported by the underlying `GEOS` library will result in a panic. +Users can use [`VersionCompare`](https://pkg.go.dev/github.com/twpayne/go-geos#VersionCompare) +to be sure that a function exists. + +## Contributing + +Please check [`CONTRIBUTING.md`](./CONTRIBUTING.md) for instructions before you open a pull-request! + ## License -MIT \ No newline at end of file +MIT diff --git a/vendor/github.com/twpayne/go-geos/bounds.go b/vendor/github.com/twpayne/go-geos/box2d.go similarity index 64% rename from vendor/github.com/twpayne/go-geos/bounds.go rename to vendor/github.com/twpayne/go-geos/box2d.go index 189671a4..14e7b0dc 100644 --- a/vendor/github.com/twpayne/go-geos/bounds.go +++ b/vendor/github.com/twpayne/go-geos/box2d.go @@ -5,17 +5,17 @@ import ( "math" ) -// A Bounds is a two-dimensional bounds. -type Bounds struct { +// A Box2D is a two-dimensional bounds. +type Box2D struct { MinX float64 MinY float64 MaxX float64 MaxY float64 } -// NewBounds returns a new bounds. -func NewBounds(minX, minY, maxX, maxY float64) *Bounds { - return &Bounds{ +// NewBox2D returns a new bounds. +func NewBox2D(minX, minY, maxX, maxY float64) *Box2D { + return &Box2D{ MinX: minX, MinY: minY, MaxX: maxX, @@ -23,9 +23,9 @@ func NewBounds(minX, minY, maxX, maxY float64) *Bounds { } } -// NewBoundsEmpty returns a new empty bounds. -func NewBoundsEmpty() *Bounds { - return &Bounds{ +// NewBox2DEmpty returns a new empty bounds. +func NewBox2DEmpty() *Box2D { + return &Box2D{ MinX: math.Inf(1), MinY: math.Inf(1), MaxX: math.Inf(-1), @@ -34,7 +34,7 @@ func NewBoundsEmpty() *Bounds { } // Contains returns true if b contains other. -func (b *Bounds) Contains(other *Bounds) bool { +func (b *Box2D) Contains(other *Box2D) bool { if b.IsEmpty() || other.IsEmpty() { return false } @@ -42,50 +42,50 @@ func (b *Bounds) Contains(other *Bounds) bool { } // ContainsPoint returns true if b contains the point at x, y. -func (b *Bounds) ContainsPoint(x, y float64) bool { +func (b *Box2D) ContainsPoint(x, y float64) bool { return b.MinX <= x && x <= b.MaxX && b.MinY <= y && y <= b.MaxY } // ContextGeom returns b as a Geom. -func (b *Bounds) ContextGeom(context *Context) *Geom { - return context.NewGeomFromBounds(b) +func (b *Box2D) ContextGeom(context *Context) *Geom { + return context.NewGeomFromBounds(b.MinX, b.MinY, b.MaxX, b.MaxY) } // Equals returns true if b equals other. -func (b *Bounds) Equals(other *Bounds) bool { +func (b *Box2D) Equals(other *Box2D) bool { return b.MinX == other.MinX && b.MinY == other.MinY && b.MaxX == other.MaxX && b.MaxY == other.MaxY } // Geom returns b as a Geom. -func (b *Bounds) Geom() *Geom { +func (b *Box2D) Geom() *Geom { return b.ContextGeom(DefaultContext) } // IsEmpty returns true if b is empty. -func (b *Bounds) IsEmpty() bool { +func (b *Box2D) IsEmpty() bool { return b.MinX > b.MaxX || b.MinY > b.MaxY } // Height returns the height of b. -func (b *Bounds) Height() float64 { +func (b *Box2D) Height() float64 { return b.MaxY - b.MinY } // Intersects returns true if b intersects other. -func (b *Bounds) Intersects(other *Bounds) bool { +func (b *Box2D) Intersects(other *Box2D) bool { return !(other.MinX > b.MaxX || other.MinY > b.MaxY || other.MaxX < b.MinX || other.MaxY < b.MinY) } // IsPoint returns true if b is a point. -func (b *Bounds) IsPoint() bool { +func (b *Box2D) IsPoint() bool { return b.MinX == b.MaxX && b.MinY == b.MaxY } -func (b *Bounds) String() string { +func (b *Box2D) String() string { return fmt.Sprintf("[%f %f %f %f]", b.MinX, b.MinY, b.MaxX, b.MaxY) } // Width returns the width of b. -func (b *Bounds) Width() float64 { +func (b *Box2D) Width() float64 { return b.MaxX - b.MinX } diff --git a/vendor/github.com/twpayne/go-geos/box3d.go b/vendor/github.com/twpayne/go-geos/box3d.go new file mode 100644 index 00000000..3f4a10c8 --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/box3d.go @@ -0,0 +1,44 @@ +package geos + +import ( + "fmt" + "math" +) + +// A Box3D is a three-dimensional bounds. +type Box3D struct { + MinX float64 + MinY float64 + MinZ float64 + MaxX float64 + MaxY float64 + MaxZ float64 +} + +// NewBox3D returns a new bounds. +func NewBox3D(minX, minY, minZ, maxX, maxY, maxZ float64) *Box3D { + return &Box3D{ + MinX: minX, + MinY: minY, + MinZ: minZ, + MaxX: maxX, + MaxY: maxY, + MaxZ: maxZ, + } +} + +// NewBox3DEmpty returns a new empty bounds. +func NewBox3DEmpty() *Box3D { + return &Box3D{ + MinX: math.Inf(1), + MinY: math.Inf(1), + MinZ: math.Inf(1), + MaxX: math.Inf(-1), + MaxY: math.Inf(-1), + MaxZ: math.Inf(-1), + } +} + +func (b *Box3D) String() string { + return fmt.Sprintf("[%f %f %f %f %f %f]", b.MinX, b.MinY, b.MinZ, b.MaxX, b.MaxY, b.MaxX) +} diff --git a/vendor/github.com/twpayne/go-geos/bufparams.go b/vendor/github.com/twpayne/go-geos/bufparams.go new file mode 100644 index 00000000..92c8c534 --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/bufparams.go @@ -0,0 +1,87 @@ +package geos + +// #include "go-geos.h" +import "C" + +import "runtime" + +// A BufParams contains parameters for BufferWithParams. +type BufParams struct { + context *Context + cBufParams *C.struct_GEOSBufParams_t +} + +// NewBufParams returns a new BufParams. +func (c *Context) NewBufParams() *BufParams { + c.mutex.Lock() + defer c.mutex.Unlock() + cBufParams := C.GEOSBufferParams_create_r(c.cHandle) + if cBufParams == nil { + panic(c.err) + } + bufParams := &BufParams{ + context: c, + cBufParams: cBufParams, + } + c.ref() + runtime.AddCleanup(bufParams, c.destroyBufParams, cBufParams) + return bufParams +} + +// SetEndCapStyle sets p's end cap style. +func (p *BufParams) SetEndCapStyle(style BufCapStyle) *BufParams { + p.context.mutex.Lock() + defer p.context.mutex.Unlock() + if C.GEOSBufferParams_setEndCapStyle_r(p.context.cHandle, p.cBufParams, C.int(style)) != 1 { + panic(p.context.err) + } + return p +} + +// SetJoinStyle sets p's join style. +func (p *BufParams) SetJoinStyle(style BufJoinStyle) *BufParams { + p.context.mutex.Lock() + defer p.context.mutex.Unlock() + if C.GEOSBufferParams_setJoinStyle_r(p.context.cHandle, p.cBufParams, C.int(style)) != 1 { + panic(p.context.err) + } + return p +} + +// SetMitreLimit sets p's mitre limit. +func (p *BufParams) SetMitreLimit(mitreLimit float64) *BufParams { + p.context.mutex.Lock() + defer p.context.mutex.Unlock() + if C.GEOSBufferParams_setMitreLimit_r(p.context.cHandle, p.cBufParams, C.double(mitreLimit)) != 1 { + panic(p.context.err) + } + return p +} + +// SetQuadrantSegments sets the number of segments to stroke each quadrant of +// circular arcs. +func (p *BufParams) SetQuadrantSegments(quadSegs int) *BufParams { + p.context.mutex.Lock() + defer p.context.mutex.Unlock() + if C.GEOSBufferParams_setQuadrantSegments_r(p.context.cHandle, p.cBufParams, C.int(quadSegs)) != 1 { + panic(p.context.err) + } + return p +} + +// SetSingleSided sets whether the computed buffer should be single sided. +func (p *BufParams) SetSingleSided(singleSided bool) *BufParams { + p.context.mutex.Lock() + defer p.context.mutex.Unlock() + if C.GEOSBufferParams_setSingleSided_r(p.context.cHandle, p.cBufParams, toInt[C.int](singleSided)) != 1 { + panic(p.context.err) + } + return p +} + +func (c *Context) destroyBufParams(cBufParams *C.struct_GEOSBufParams_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSBufferParams_destroy_r(c.cHandle, cBufParams) + c.unref() +} diff --git a/vendor/github.com/twpayne/go-geos/context.go b/vendor/github.com/twpayne/go-geos/context.go index e7762dd3..5a75d930 100644 --- a/vendor/github.com/twpayne/go-geos/context.go +++ b/vendor/github.com/twpayne/go-geos/context.go @@ -6,60 +6,75 @@ import "C" import ( "runtime" + "runtime/cgo" "sync" + "sync/atomic" "unsafe" ) // A Context is a context. type Context struct { - sync.Mutex - handle C.GEOSContextHandle_t - geoJSONReader *C.struct_GEOSGeoJSONReader_t - geoJSONWriter *C.struct_GEOSGeoJSONWriter_t - wkbReader *C.struct_GEOSWKBReader_t - wkbWriter *C.struct_GEOSWKBWriter_t - wktReader *C.struct_GEOSWKTReader_t - wktWriter *C.struct_GEOSWKTWriter_t - err error - geomFinalizeFunc func(*Geom) - strTreeFinalizeFunc func(*STRtree) -} - -// A ContextOption sets an option on a Context. -type ContextOption func(*Context) - -// WithGeomFinalizeFunc sets a function to be called just before a geometry is -// finalized. This is typically used to log the geometry to help debug geometry -// leaks. -func WithGeomFinalizeFunc(geomFinalizeFunc func(*Geom)) ContextOption { - return func(c *Context) { - c.geomFinalizeFunc = geomFinalizeFunc - } -} - -// WithSTRtreeFinalizeFunc sets a function to be called just before an STRtree -// is finalized. This is typically used to log the STRtree to help debug STRtree -// leaks. -func WithSTRtreeFinalizeFunc(strTreeFinalizeFunc func(*STRtree)) ContextOption { - return func(c *Context) { - c.strTreeFinalizeFunc = strTreeFinalizeFunc - } + mutex sync.Mutex + cHandle C.GEOSContextHandle_t + refCount *atomic.Int64 + ewkbWithSRIDWriter func() *WKBWriter + geoJSONReader func() *GeoJSONReader + geoJSONWriter func() *GeoJSONWriter + wkbWriter func() *WKBWriter + wkbReader func() *WKBReader + wktReader func() *WKTReader + wktWriter func() *WKTWriter + err error } // NewContext returns a new Context. -func NewContext(options ...ContextOption) *Context { +func NewContext() *Context { + cHandle := C.GEOS_init_r() + var refCount atomic.Int64 c := &Context{ - handle: C.GEOS_init_r(), - } - runtime.SetFinalizer(c, (*Context).finish) - // FIXME in GitHub Actions, golangci-lint complains about the following line saying: - // Error: dupSubExpr: suspicious identical LHS and RHS for `==` operator (gocritic) - // As the line does not contain an `==` operator, disable gocritic on this line. + cHandle: cHandle, + refCount: &refCount, + } + c.ref() + runtime.AddCleanup(c, func(cHandle C.GEOSContextHandle_t) { + // Inline unref here so that the cleanup function does not hold a + // reference to c. + if refCount.Add(-1) == 0 { + C.finishGEOS_r(cHandle) + } + }, cHandle) + c.ewkbWithSRIDWriter = sync.OnceValue(func() *WKBWriter { + return c.NewWKBWriter( + WithWKBWriterFlavor(WKBFlavorExtended), + WithWKBWriterIncludeSRID(true), + ) + }) + c.geoJSONReader = sync.OnceValue(func() *GeoJSONReader { + return c.NewGeoJSONReader() + }) + c.geoJSONWriter = sync.OnceValue(func() *GeoJSONWriter { + return c.NewGeoJSONWriter() + }) + c.wkbReader = sync.OnceValue(func() *WKBReader { + return c.NewWKBReader() + }) + c.wkbWriter = sync.OnceValue(func() *WKBWriter { + return c.NewWKBWriter() + }) + c.wktReader = sync.OnceValue(func() *WKTReader { + return c.NewWKTReader() + }) + c.wktWriter = sync.OnceValue(func() *WKTWriter { + return c.NewWKTWriter() + }) + errPHandle := cgo.NewHandle(&c.err) + runtime.AddCleanup(c, cgo.Handle.Delete, errPHandle) + // FIXME golangci-lint complains about the following line saying: Error: + // dupSubExpr: suspicious identical LHS and RHS for `==` operator (gocritic) + // As the line does not contain an `==` operator, disable gocritic on this + // line. //nolint:gocritic - C.GEOSContext_setErrorMessageHandler_r(c.handle, C.GEOSMessageHandler_r(C.c_errorMessageHandler), unsafe.Pointer(&c.err)) - for _, option := range options { - option(c) - } + C.GEOSContext_setErrorMessageHandler_r(c.cHandle, C.GEOSMessageHandler_r(C.c_errorMessageHandler), unsafe.Pointer(&errPHandle)) return c } @@ -71,246 +86,53 @@ func (c *Context) Clone(g *Geom) *Geom { // FIXME use a more intelligent method than a WKB roundtrip (although a WKB // roundtrip might actually be quite fast if the cgo overhead is // significant) - clone, err := c.NewGeomFromWKB(g.ToWKB()) + clone, err := c.NewGeomFromWKB(g.ToEWKBWithSRID()) if err != nil { panic(err) } return clone } -// NewCollection returns a new collection. -func (c *Context) NewCollection(typeID TypeID, geoms []*Geom) *Geom { - if len(geoms) == 0 { - return c.NewEmptyCollection(typeID) - } - c.Lock() - defer c.Unlock() - cGeoms := make([]*C.GEOSGeometry, len(geoms)) - for i, geom := range geoms { - cGeoms[i] = geom.geom - } - g := c.newNonNilGeom(C.GEOSGeom_createCollection_r(c.handle, C.int(typeID), &cGeoms[0], C.uint(len(geoms))), nil) - for _, geom := range geoms { - geom.parent = g - } - return g -} - -// NewCoordSeq returns a new CoordSeq. -func (c *Context) NewCoordSeq(size, dims int) *CoordSeq { - c.Lock() - defer c.Unlock() - return c.newNonNilCoordSeq(C.GEOSCoordSeq_create_r(c.handle, C.uint(size), C.uint(dims))) -} - -// NewCoordSeqFromCoords returns a new CoordSeq populated with coords. -func (c *Context) NewCoordSeqFromCoords(coords [][]float64) *CoordSeq { - c.Lock() - defer c.Unlock() - return c.newNonNilCoordSeq(c.newGEOSCoordSeqFromCoords(coords)) -} - -// NewEmptyCollection returns a new empty collection. -func (c *Context) NewEmptyCollection(typeID TypeID) *Geom { - c.Lock() - defer c.Unlock() - return c.newNonNilGeom(C.GEOSGeom_createEmptyCollection_r(c.handle, C.int(typeID)), nil) -} - -// NewEmptyLineString returns a new empty line string. -func (c *Context) NewEmptyLineString() *Geom { - c.Lock() - defer c.Unlock() - return c.newNonNilGeom(C.GEOSGeom_createEmptyLineString_r(c.handle), nil) -} - -// NewEmptyPoint returns a new empty point. -func (c *Context) NewEmptyPoint() *Geom { - c.Lock() - defer c.Unlock() - return c.newNonNilGeom(C.GEOSGeom_createEmptyPoint_r(c.handle), nil) -} - -// NewEmptyPolygon returns a new empty polygon. -func (c *Context) NewEmptyPolygon() *Geom { - c.Lock() - defer c.Unlock() - return c.newNonNilGeom(C.GEOSGeom_createEmptyPolygon_r(c.handle), nil) -} - -// NewGeomFromBounds returns a new polygon constructed from bounds. -func (c *Context) NewGeomFromBounds(bounds *Bounds) *Geom { - var typeID C.int - geom := C.c_newGEOSGeomFromBounds_r(c.handle, &typeID, C.double(bounds.MinX), C.double(bounds.MinY), C.double(bounds.MaxX), C.double(bounds.MaxY)) - if geom == nil { - panic(c.err) - } - g := &Geom{ - context: c, - geom: geom, - typeID: TypeID(typeID), - numGeometries: 1, - } - runtime.SetFinalizer(g, (*Geom).finalize) - return g -} - // NewGeomFromGeoJSON returns a new geometry in JSON format from json. func (c *Context) NewGeomFromGeoJSON(geoJSON string) (*Geom, error) { - c.Lock() - defer c.Unlock() - c.err = nil - if c.geoJSONReader == nil { - c.geoJSONReader = C.GEOSGeoJSONReader_create_r(c.handle) - } - geoJSONCStr := C.CString(geoJSON) - defer C.free(unsafe.Pointer(geoJSONCStr)) - return c.newGeom(C.GEOSGeoJSONReader_readGeometry_r(c.handle, c.geoJSONReader, geoJSONCStr), nil), c.err + return c.geoJSONReader().ReadGeometry(geoJSON) } // NewGeomFromWKB parses a geometry in WKB format from wkb. func (c *Context) NewGeomFromWKB(wkb []byte) (*Geom, error) { - c.Lock() - defer c.Unlock() - c.err = nil - if c.wkbReader == nil { - c.wkbReader = C.GEOSWKBReader_create_r(c.handle) - } - wkbCBuf := C.CBytes(wkb) - defer C.free(wkbCBuf) - return c.newGeom(C.GEOSWKBReader_read_r(c.handle, c.wkbReader, (*C.uchar)(wkbCBuf), C.ulong(len(wkb))), nil), c.err + return c.wkbReader().Read(wkb) } // NewGeomFromWKT parses a geometry in WKT format from wkt. func (c *Context) NewGeomFromWKT(wkt string) (*Geom, error) { - c.Lock() - defer c.Unlock() - c.err = nil - if c.wktReader == nil { - c.wktReader = C.GEOSWKTReader_create_r(c.handle) - } - wktCStr := C.CString(wkt) - defer C.free(unsafe.Pointer(wktCStr)) - return c.newGeom(C.GEOSWKTReader_read_r(c.handle, c.wktReader, wktCStr), nil), c.err -} - -// NewLinearRing returns a new linear ring populated with coords. -func (c *Context) NewLinearRing(coords [][]float64) *Geom { - c.Lock() - defer c.Unlock() - s := c.newGEOSCoordSeqFromCoords(coords) - return c.newNonNilGeom(C.GEOSGeom_createLinearRing_r(c.handle, s), nil) -} - -// NewLineString returns a new line string populated with coords. -func (c *Context) NewLineString(coords [][]float64) *Geom { - c.Lock() - defer c.Unlock() - s := c.newGEOSCoordSeqFromCoords(coords) - return c.newNonNilGeom(C.GEOSGeom_createLineString_r(c.handle, s), nil) -} - -// NewPoint returns a new point populated with coord. -func (c *Context) NewPoint(coord []float64) *Geom { - s := c.newGEOSCoordSeqFromCoords([][]float64{coord}) - return c.newNonNilGeom(C.GEOSGeom_createPoint_r(c.handle, s), nil) -} - -// NewPointFromXY returns a new point with a x and y. -func (c *Context) NewPointFromXY(x, y float64) *Geom { - return c.newNonNilGeom(C.GEOSGeom_createPointFromXY_r(c.handle, C.double(x), C.double(y)), nil) -} - -// NewPoints returns a new slice of points populated from coords. -func (c *Context) NewPoints(coords [][]float64) []*Geom { - if coords == nil { - return nil - } - geoms := make([]*Geom, 0, len(coords)) - for _, coord := range coords { - geom := c.NewPoint(coord) - geoms = append(geoms, geom) - } - return geoms -} - -// NewPolygon returns a new point populated with coordss. -func (c *Context) NewPolygon(coordss [][][]float64) *Geom { - if len(coordss) == 0 { - return c.NewEmptyPolygon() - } - var ( - shell *C.struct_GEOSGeom_t - holesSlice []*C.struct_GEOSGeom_t - ) - defer func() { - if v := recover(); v != nil { - C.GEOSGeom_destroy_r(c.handle, shell) - for _, hole := range holesSlice { - C.GEOSGeom_destroy_r(c.handle, hole) - } - panic(v) - } - }() - shell = C.GEOSGeom_createLinearRing_r(c.handle, c.newGEOSCoordSeqFromCoords(coordss[0])) - if shell == nil { - panic(c.err) - } - var holes **C.struct_GEOSGeom_t - nholes := len(coordss) - 1 - if nholes > 0 { - holesSlice = make([]*C.struct_GEOSGeom_t, 0, nholes) - for i := 0; i < nholes; i++ { - hole := C.GEOSGeom_createLinearRing_r(c.handle, c.newGEOSCoordSeqFromCoords(coordss[i+1])) - if hole == nil { - panic(c.err) - } - holesSlice = append(holesSlice, hole) - } - holes = (**C.struct_GEOSGeom_t)(unsafe.Pointer(&holesSlice[0])) - } - return c.newNonNilGeom(C.GEOSGeom_createPolygon_r(c.handle, shell, holes, C.uint(nholes)), nil) -} - -// NewSTRtree returns a new STRtree. -func (c *Context) NewSTRtree(nodeCapacity int) *STRtree { - c.Lock() - defer c.Unlock() - t := &STRtree{ - context: c, - strTree: C.GEOSSTRtree_create_r(c.handle, C.size_t(nodeCapacity)), - itemToValue: make(map[unsafe.Pointer]any), - valueToItem: make(map[any]unsafe.Pointer), - } - runtime.SetFinalizer(t, (*STRtree).finalize) - return t + return c.wktReader().Read(wkt) } // OrientationIndex returns the orientation index from A to B and then to P. -func (c *Context) OrientationIndex(Ax, Ay, Bx, By, Px, Py float64) int { //nolint:gocritic - c.Lock() - defer c.Unlock() - return int(C.GEOSOrientationIndex_r(c.handle, C.double(Ax), C.double(Ay), C.double(Bx), C.double(By), C.double(Px), C.double(Py))) +func (c *Context) OrientationIndex(ax, ay, bx, by, px, py float64) int { + c.mutex.Lock() + defer c.mutex.Unlock() + return int(C.GEOSOrientationIndex_r(c.cHandle, C.double(ax), C.double(ay), C.double(bx), C.double(by), C.double(px), C.double(py))) } // Polygonize returns a set of geometries which contains linework that // represents the edges of a planar graph. func (c *Context) Polygonize(geoms []*Geom) *Geom { - c.Lock() - defer c.Unlock() + c.mutex.Lock() + defer c.mutex.Unlock() cGeoms, unlockFunc := c.cGeomsLocked(geoms) defer unlockFunc() - return c.newNonNilGeom(C.GEOSPolygonize_r(c.handle, cGeoms, C.uint(len(geoms))), nil) + return c.newNonNilGeom(C.GEOSPolygonize_r(c.cHandle, cGeoms, C.uint(len(geoms))), nil) } // PolygonizeValid returns a set of polygons which contains linework that // represents the edges of a planar graph. func (c *Context) PolygonizeValid(geoms []*Geom) *Geom { - c.Lock() - defer c.Unlock() + c.mutex.Lock() + defer c.mutex.Unlock() cGeoms, unlockFunc := c.cGeomsLocked(geoms) defer unlockFunc() - return c.newNonNilGeom(C.GEOSPolygonize_valid_r(c.handle, cGeoms, C.uint(len(geoms))), nil) + return c.newNonNilGeom(C.GEOSPolygonize_valid_r(c.cHandle, cGeoms, C.uint(len(geoms))), nil) } // RelatePatternMatch returns if two DE9IM patterns are consistent. @@ -319,9 +141,9 @@ func (c *Context) RelatePatternMatch(mat, pat string) bool { defer C.free(unsafe.Pointer(matCStr)) patCStr := C.CString(pat) defer C.free(unsafe.Pointer(patCStr)) - c.Lock() - defer c.Unlock() - switch C.GEOSRelatePatternMatch_r(c.handle, matCStr, patCStr) { + c.mutex.Lock() + defer c.mutex.Unlock() + switch C.GEOSRelatePatternMatch_r(c.cHandle, matCStr, patCStr) { case 0: return false case 1: @@ -332,11 +154,11 @@ func (c *Context) RelatePatternMatch(mat, pat string) bool { } // SegmentIntersection returns the coordinate where two lines intersect. -func (c *Context) SegmentIntersection(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1 float64) (float64, float64, bool) { - c.Lock() - defer c.Unlock() +func (c *Context) SegmentIntersection(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1 float64) (x, y float64, intersection bool) { + c.mutex.Lock() + defer c.mutex.Unlock() var cx, cy float64 - switch C.GEOSSegmentIntersection_r(c.handle, + switch C.GEOSSegmentIntersection_r(c.cHandle, C.double(ax0), C.double(ay0), C.double(ax1), C.double(ay1), C.double(bx0), C.double(by0), C.double(bx1), C.double(by1), (*C.double)(&cx), (*C.double)(&cy)) { @@ -355,166 +177,39 @@ func (c *Context) cGeomsLocked(geoms []*Geom) (**C.struct_GEOSGeom_t, func()) { } uniqueContexts := map[*Context]struct{}{c: {}} var extraContexts []*Context - cGeoms := make([]*C.struct_GEOSGeom_t, 0, len(geoms)) - for _, geom := range geoms { - geom.mustNotBeDestroyed() + cGeoms := make([]*C.struct_GEOSGeom_t, len(geoms)) + for i := range cGeoms { + geom := geoms[i] if _, ok := uniqueContexts[geom.context]; !ok { - geom.context.Lock() + geom.context.mutex.Lock() uniqueContexts[geom.context] = struct{}{} extraContexts = append(extraContexts, geom.context) } - cGeoms = append(cGeoms, geom.geom) + cGeoms[i] = geom.cGeom } return &cGeoms[0], func() { for i := len(extraContexts) - 1; i >= 0; i-- { - extraContexts[i].Unlock() + extraContexts[i].mutex.Unlock() } } } -func (c *Context) finish() { - c.Lock() - defer c.Unlock() - if c.geoJSONReader != nil { - C.GEOSGeoJSONReader_destroy_r(c.handle, c.geoJSONReader) - } - if c.geoJSONWriter != nil { - C.GEOSGeoJSONWriter_destroy_r(c.handle, c.geoJSONWriter) - } - if c.wkbReader != nil { - C.GEOSWKBReader_destroy_r(c.handle, c.wkbReader) - } - if c.wkbWriter != nil { - C.GEOSWKBWriter_destroy_r(c.handle, c.wkbWriter) - } - if c.wktReader != nil { - C.GEOSWKTReader_destroy_r(c.handle, c.wktReader) - } - if c.wktWriter != nil { - C.GEOSWKTWriter_destroy_r(c.handle, c.wktWriter) - } - C.finishGEOS_r(c.handle) +// ref increases c's reference count by 1. +func (c *Context) ref() { + c.refCount.Add(1) } -func (c *Context) newCoordSeq(gs *C.struct_GEOSCoordSeq_t, finalizer func(*CoordSeq)) *CoordSeq { - if gs == nil { - return nil - } - var ( - dimensions C.uint - size C.uint - ) - if C.GEOSCoordSeq_getDimensions_r(c.handle, gs, &dimensions) == 0 { - panic(c.err) - } - if C.GEOSCoordSeq_getSize_r(c.handle, gs, &size) == 0 { - panic(c.err) - } - s := &CoordSeq{ - context: c, - s: gs, - dimensions: int(dimensions), - size: int(size), - } - if finalizer != nil { - runtime.SetFinalizer(s, finalizer) - } - return s -} - -func (c *Context) newCoordsFromGEOSCoordSeq(s *C.struct_GEOSCoordSeq_t) [][]float64 { - var dimensions C.uint - if C.GEOSCoordSeq_getDimensions_r(c.handle, s, &dimensions) == 0 { - panic(c.err) - } - - var size C.uint - if C.GEOSCoordSeq_getSize_r(c.handle, s, &size) == 0 { - panic(c.err) - } - - var hasZ C.int - if dimensions > 2 { - hasZ = 1 - } - - var hasM C.int - if dimensions > 3 { - hasM = 1 - } - - flatCoords := make([]float64, size*dimensions) - if C.GEOSCoordSeq_copyToBuffer_r(c.handle, s, (*C.double)(&flatCoords[0]), hasZ, hasM) == 0 { - panic(c.err) - } - coords := make([][]float64, 0, size) - for i := 0; i < int(size); i++ { - coord := flatCoords[i*int(dimensions) : (i+1)*int(dimensions)] - coords = append(coords, coord) - } - return coords -} - -func (c *Context) newGEOSCoordSeqFromCoords(coords [][]float64) *C.struct_GEOSCoordSeq_t { - var hasZ C.int - if len(coords[0]) > 2 { - hasZ = 1 - } - - var hasM C.int - if len(coords[0]) > 3 { - hasM = 1 - } - - flatCoords := make([]float64, 0, len(coords)*len(coords[0])) - for _, coord := range coords { - flatCoords = append(flatCoords, coord...) - } - return C.GEOSCoordSeq_copyFromBuffer_r(c.handle, (*C.double)(unsafe.Pointer(&flatCoords[0])), C.uint(len(coords)), hasZ, hasM) -} - -func (c *Context) newGeom(geom *C.struct_GEOSGeom_t, parent *Geom) *Geom { - if geom == nil { - return nil - } - var ( - typeID C.int - numGeometries C.int - numPoints C.int - numInteriorRings C.int - ) - if C.c_GEOSGeomGetInfo_r(c.handle, geom, &typeID, &numGeometries, &numPoints, &numInteriorRings) == 0 { - panic(c.err) - } - g := &Geom{ - context: c, - geom: geom, - parent: parent, - typeID: TypeID(typeID), - numGeometries: int(numGeometries), - numInteriorRings: int(numInteriorRings), - numPoints: int(numPoints), - } - runtime.SetFinalizer(g, (*Geom).finalize) - return g -} - -func (c *Context) newNonNilCoordSeq(s *C.struct_GEOSCoordSeq_t) *CoordSeq { - if s == nil { - panic(c.err) - } - return c.newCoordSeq(s, (*CoordSeq).destroy) -} - -func (c *Context) newNonNilGeom(geom *C.struct_GEOSGeom_t, parent *Geom) *Geom { - if geom == nil { - panic(c.err) +// unref decreases c's reference count by 1 and finishes c if its reference +// count becomes zero. +func (c *Context) unref() { + if c.refCount.Add(-1) == 0 { + C.finishGEOS_r(c.cHandle) } - return c.newGeom(geom, parent) } //export go_errorMessageHandler func go_errorMessageHandler(message *C.char, userdata unsafe.Pointer) { - errP := (*error)(userdata) + errPHandle := (*cgo.Handle)(userdata) + errP := errPHandle.Value().(*error) //nolint:forcetypeassert,revive *errP = Error(C.GoString(message)) } diff --git a/vendor/github.com/twpayne/go-geos/coordseq.go b/vendor/github.com/twpayne/go-geos/coordseq.go index 20851265..a25b57a5 100644 --- a/vendor/github.com/twpayne/go-geos/coordseq.go +++ b/vendor/github.com/twpayne/go-geos/coordseq.go @@ -3,20 +3,39 @@ package geos // #include "go-geos.h" import "C" +import ( + "runtime" + "unsafe" +) + // A CoordSeq is a coordinate sequence. type CoordSeq struct { context *Context s *C.struct_GEOSCoordSeq_t - parent *Geom + owner *Geom dimensions int size int } +// NewCoordSeq returns a new CoordSeq. +func (c *Context) NewCoordSeq(size, dims int) *CoordSeq { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.newNonNilCoordSeq(C.GEOSCoordSeq_create_r(c.cHandle, C.uint(size), C.uint(dims))) +} + +// NewCoordSeqFromCoords returns a new CoordSeq populated with coords. +func (c *Context) NewCoordSeqFromCoords(coords [][]float64) *CoordSeq { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.newNonNilCoordSeq(c.newGEOSCoordSeqFromCoords(coords)) +} + // Clone returns a clone of s. func (s *CoordSeq) Clone() *CoordSeq { - s.context.Lock() - defer s.context.Unlock() - return s.context.newNonNilCoordSeq(C.GEOSCoordSeq_clone_r(s.context.handle, s.s)) + s.context.mutex.Lock() + defer s.context.mutex.Unlock() + return s.context.newNonNilCoordSeq(C.GEOSCoordSeq_clone_r(s.context.cHandle, s.s)) } // Dimensions returns the dimensions of s. @@ -26,10 +45,10 @@ func (s *CoordSeq) Dimensions() int { // IsCCW returns if s is counter-clockwise. func (s *CoordSeq) IsCCW() bool { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() var cIsCCW C.char - switch C.GEOSCoordSeq_isCCW_r(s.context.handle, s.s, &cIsCCW) { + switch C.GEOSCoordSeq_isCCW_r(s.context.cHandle, s.s, &cIsCCW) { case 1: return cIsCCW != 0 default: @@ -39,8 +58,8 @@ func (s *CoordSeq) IsCCW() bool { // Ordinate returns the idx-th dim coordinate of s. func (s *CoordSeq) Ordinate(idx, dim int) float64 { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } @@ -48,7 +67,7 @@ func (s *CoordSeq) Ordinate(idx, dim int) float64 { panic(errDimensionOutOfRange) } var value float64 - if C.GEOSCoordSeq_getOrdinate_r(s.context.handle, s.s, C.uint(idx), C.uint(dim), (*C.double)(&value)) == 0 { + if C.GEOSCoordSeq_getOrdinate_r(s.context.cHandle, s.s, C.uint(idx), C.uint(dim), (*C.double)(&value)) == 0 { panic(s.context.err) } return value @@ -56,60 +75,60 @@ func (s *CoordSeq) Ordinate(idx, dim int) float64 { // SetOrdinate sets the idx-th dim coordinate of s to val. func (s *CoordSeq) SetOrdinate(idx, dim int, val float64) { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } if dim < 0 || s.dimensions <= dim { panic(errDimensionOutOfRange) } - if C.GEOSCoordSeq_setOrdinate_r(s.context.handle, s.s, C.uint(idx), C.uint(dim), C.double(val)) == 0 { + if C.GEOSCoordSeq_setOrdinate_r(s.context.cHandle, s.s, C.uint(idx), C.uint(dim), C.double(val)) == 0 { panic(s.context.err) } } // SetX sets the idx-th X coordinate of s to val. func (s *CoordSeq) SetX(idx int, val float64) { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } if s.dimensions == 0 { panic(errDimensionOutOfRange) } - if C.GEOSCoordSeq_setX_r(s.context.handle, s.s, C.uint(idx), C.double(val)) == 0 { + if C.GEOSCoordSeq_setX_r(s.context.cHandle, s.s, C.uint(idx), C.double(val)) == 0 { panic(s.context.err) } } // SetY sets the idx-th Y coordinate of s to val. func (s *CoordSeq) SetY(idx int, val float64) { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } if s.dimensions < 2 { panic(errDimensionOutOfRange) } - if C.GEOSCoordSeq_setY_r(s.context.handle, s.s, C.uint(idx), C.double(val)) == 0 { + if C.GEOSCoordSeq_setY_r(s.context.cHandle, s.s, C.uint(idx), C.double(val)) == 0 { panic(s.context.err) } } // SetZ sets the idx-th Z coordinate of s to val. func (s *CoordSeq) SetZ(idx int, val float64) { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } if s.dimensions < 3 { panic(errDimensionOutOfRange) } - if C.GEOSCoordSeq_setZ_r(s.context.handle, s.s, C.uint(idx), C.double(val)) == 0 { + if C.GEOSCoordSeq_setZ_r(s.context.cHandle, s.s, C.uint(idx), C.double(val)) == 0 { panic(s.context.err) } } @@ -121,8 +140,8 @@ func (s *CoordSeq) Size() int { // ToCoords returns s as a [][]float64. func (s *CoordSeq) ToCoords() [][]float64 { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if s.size == 0 || s.dimensions == 0 { return nil } @@ -135,13 +154,13 @@ func (s *CoordSeq) ToCoords() [][]float64 { if s.dimensions > 3 { hasM = 1 } - if C.GEOSCoordSeq_copyToBuffer_r(s.context.handle, s.s, (*C.double)(&flatCoords[0]), hasZ, hasM) == 0 { + if C.GEOSCoordSeq_copyToBuffer_r(s.context.cHandle, s.s, (*C.double)(&flatCoords[0]), hasZ, hasM) == 0 { panic(s.context.err) } coords := make([][]float64, s.size) j := 0 - for i := 0; i < s.size; i++ { - coords[i] = flatCoords[j : j+s.dimensions] + for i := range s.size { + coords[i] = flatCoords[j : j+s.dimensions : j+s.dimensions] j += s.dimensions } return coords @@ -149,8 +168,8 @@ func (s *CoordSeq) ToCoords() [][]float64 { // X returns the idx-th X coordinate of s. func (s *CoordSeq) X(idx int) float64 { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } @@ -158,7 +177,7 @@ func (s *CoordSeq) X(idx int) float64 { panic(errDimensionOutOfRange) } var val float64 - if C.GEOSCoordSeq_getX_r(s.context.handle, s.s, C.uint(idx), (*C.double)(&val)) == 0 { + if C.GEOSCoordSeq_getX_r(s.context.cHandle, s.s, C.uint(idx), (*C.double)(&val)) == 0 { panic(s.context.err) } return val @@ -166,8 +185,8 @@ func (s *CoordSeq) X(idx int) float64 { // Y returns the idx-th Y coordinate of s. func (s *CoordSeq) Y(idx int) float64 { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } @@ -175,7 +194,7 @@ func (s *CoordSeq) Y(idx int) float64 { panic(errDimensionOutOfRange) } var val float64 - if C.GEOSCoordSeq_getY_r(s.context.handle, s.s, C.uint(idx), (*C.double)(&val)) == 0 { + if C.GEOSCoordSeq_getY_r(s.context.cHandle, s.s, C.uint(idx), (*C.double)(&val)) == 0 { panic(s.context.err) } return val @@ -183,8 +202,8 @@ func (s *CoordSeq) Y(idx int) float64 { // Z returns the idx-th Z coordinate of s. func (s *CoordSeq) Z(idx int) float64 { - s.context.Lock() - defer s.context.Unlock() + s.context.mutex.Lock() + defer s.context.mutex.Unlock() if idx < 0 || s.size <= idx { panic(errIndexOutOfRange) } @@ -192,15 +211,102 @@ func (s *CoordSeq) Z(idx int) float64 { panic(errDimensionOutOfRange) } var val float64 - if C.GEOSCoordSeq_getZ_r(s.context.handle, s.s, C.uint(idx), (*C.double)(&val)) == 0 { + if C.GEOSCoordSeq_getZ_r(s.context.cHandle, s.s, C.uint(idx), (*C.double)(&val)) == 0 { panic(s.context.err) } return val } -func (s *CoordSeq) destroy() { - s.context.Lock() - defer s.context.Unlock() - C.GEOSCoordSeq_destroy_r(s.context.handle, s.s) - *s = CoordSeq{} // Clear all references. +func (c *Context) newCoordSeqInternal(cCoordSeq *C.struct_GEOSCoordSeq_t, owner *Geom) *CoordSeq { + if cCoordSeq == nil { + return nil + } + var ( + dimensions C.uint + size C.uint + ) + if C.GEOSCoordSeq_getDimensions_r(c.cHandle, cCoordSeq, &dimensions) == 0 { + panic(c.err) + } + if C.GEOSCoordSeq_getSize_r(c.cHandle, cCoordSeq, &size) == 0 { + panic(c.err) + } + coordSeq := &CoordSeq{ + context: c, + s: cCoordSeq, + owner: owner, + dimensions: int(dimensions), + size: int(size), + } + if owner == nil { + c.ref() + runtime.AddCleanup(coordSeq, c.destroyCoordSeq, cCoordSeq) + } + return coordSeq +} + +func (c *Context) newCoordsFromGEOSCoordSeq(cCoordSeq *C.struct_GEOSCoordSeq_t) [][]float64 { + var dimensions C.uint + if C.GEOSCoordSeq_getDimensions_r(c.cHandle, cCoordSeq, &dimensions) == 0 { + panic(c.err) + } + + var size C.uint + if C.GEOSCoordSeq_getSize_r(c.cHandle, cCoordSeq, &size) == 0 { + panic(c.err) + } + + var hasZ C.int + if dimensions > 2 { + hasZ = 1 + } + + var hasM C.int + if dimensions > 3 { + hasM = 1 + } + + flatCoords := make([]float64, size*dimensions) + if C.GEOSCoordSeq_copyToBuffer_r(c.cHandle, cCoordSeq, (*C.double)(&flatCoords[0]), hasZ, hasM) == 0 { + panic(c.err) + } + coords := make([][]float64, size) + for i := range coords { + coord := flatCoords[i*int(dimensions) : (i+1)*int(dimensions) : (i+1)*int(dimensions)] + coords[i] = coord + } + return coords +} + +func (c *Context) newGEOSCoordSeqFromCoords(coords [][]float64) *C.struct_GEOSCoordSeq_t { + var hasZ C.int + if len(coords[0]) > 2 { + hasZ = 1 + } + + var hasM C.int + if len(coords[0]) > 3 { + hasM = 1 + } + + dimensions := len(coords[0]) + flatCoords := make([]float64, len(coords)*dimensions) + for i, coord := range coords { + copy(flatCoords[i*dimensions:(i+1)*dimensions], coord) + } + return C.GEOSCoordSeq_copyFromBuffer_r(c.cHandle, (*C.double)(unsafe.Pointer(&flatCoords[0])), C.uint(len(coords)), hasZ, hasM) +} + +func (c *Context) newNonNilCoordSeq(cCoordSeq *C.struct_GEOSCoordSeq_t) *CoordSeq { + if cCoordSeq == nil { + panic(c.err) + } + return c.newCoordSeqInternal(cCoordSeq, nil) +} + +func (c *Context) destroyCoordSeq(cCoordSeq *C.struct_GEOSCoordSeq_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSCoordSeq_destroy_r(c.cHandle, cCoordSeq) + c.unref() } diff --git a/vendor/github.com/twpayne/go-geos/defaultcontext.go b/vendor/github.com/twpayne/go-geos/defaultcontext.go index 602ae2b9..629216a2 100644 --- a/vendor/github.com/twpayne/go-geos/defaultcontext.go +++ b/vendor/github.com/twpayne/go-geos/defaultcontext.go @@ -9,8 +9,8 @@ func Clone(g *Geom) *Geom { } // NewGeomFromBounds returns a new polygon populated with bounds. -func NewGeomFromBounds(bounds *Bounds) *Geom { - return DefaultContext.NewGeomFromBounds(bounds) +func NewGeomFromBounds(minX, minY, maxX, maxY float64) *Geom { + return DefaultContext.NewGeomFromBounds(minX, minY, maxX, maxY) } // NewCollection returns a new collection. @@ -48,7 +48,7 @@ func NewEmptyPolygon() *Geom { return DefaultContext.NewEmptyPolygon() } -// NewGEOMFromGeoJSON parses a geometry in GeoJSON format from geoJSON. +// NewGeomFromGeoJSON parses a geometry in GeoJSON format from GeoJSON. func NewGeomFromGeoJSON(geoJSON string) (*Geom, error) { return DefaultContext.NewGeomFromGeoJSON(geoJSON) } @@ -83,11 +83,16 @@ func NewPointFromXY(x, y float64) *Geom { return DefaultContext.NewPointFromXY(x, y) } -// NewPolygon returns a new point populated with coordss. +// NewPolygon returns a new polygon populated with coordss. func NewPolygon(coordss [][][]float64) *Geom { return DefaultContext.NewPolygon(coordss) } +// NewSTRtree returns a new STRtree with the given number of entries per node. +func NewSTRtree(nodeCapacity int) *STRtree { + return DefaultContext.NewSTRtree(nodeCapacity) +} + // Polygonize returns a set of geometries which contains linework that // represents the edges of a planar graph. func Polygonize(geoms []*Geom) *Geom { diff --git a/vendor/github.com/twpayne/go-geos/geojsonreader.go b/vendor/github.com/twpayne/go-geos/geojsonreader.go new file mode 100644 index 00000000..5186205f --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/geojsonreader.go @@ -0,0 +1,47 @@ +package geos + +// #include +// #include "go-geos.h" +import "C" + +import ( + "runtime" + "unsafe" +) + +// A GeoJSONReader reads GeoJSON. +type GeoJSONReader struct { + context *Context + cGeoJSONReader *C.struct_GEOSGeoJSONReader_t +} + +// NewGeoJSONReader returns a new GeoJSONReader. +func (c *Context) NewGeoJSONReader() *GeoJSONReader { + c.mutex.Lock() + defer c.mutex.Unlock() + cGeoJSONReader := C.GEOSGeoJSONReader_create_r(c.cHandle) + geoJSONReader := &GeoJSONReader{ + context: c, + cGeoJSONReader: cGeoJSONReader, + } + c.ref() + runtime.AddCleanup(geoJSONReader, c.destroyGeoJSONReader, cGeoJSONReader) + return geoJSONReader +} + +// ReadGeometry reads a geometry from geoJSON. +func (r *GeoJSONReader) ReadGeometry(geoJSON string) (*Geom, error) { + r.context.mutex.Lock() + defer r.context.mutex.Unlock() + geoJSONCStr := C.CString(geoJSON) + defer C.free(unsafe.Pointer(geoJSONCStr)) + r.context.err = nil + return r.context.newGeom(C.GEOSGeoJSONReader_readGeometry_r(r.context.cHandle, r.cGeoJSONReader, geoJSONCStr), nil), r.context.err +} + +func (c *Context) destroyGeoJSONReader(cGeoJSONReader *C.struct_GEOSGeoJSONReader_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSGeoJSONReader_destroy_r(c.cHandle, cGeoJSONReader) + c.unref() +} diff --git a/vendor/github.com/twpayne/go-geos/geojsonwriter.go b/vendor/github.com/twpayne/go-geos/geojsonwriter.go new file mode 100644 index 00000000..017dd789 --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/geojsonwriter.go @@ -0,0 +1,46 @@ +package geos + +// #include +// #include "go-geos.h" +import "C" + +import ( + "runtime" + "unsafe" +) + +// A GeoJSONWriter writes geometries as GeoJSON. +type GeoJSONWriter struct { + context *Context + cGeoJSONWriter *C.struct_GEOSGeoJSONWriter_t +} + +// NewGeoJSONWriter returns a new GeoJSONWriter. +func (c *Context) NewGeoJSONWriter() *GeoJSONWriter { + c.mutex.Lock() + defer c.mutex.Unlock() + cGeoJSONWriter := C.GEOSGeoJSONWriter_create_r(c.cHandle) + geoJSONWriter := &GeoJSONWriter{ + context: c, + cGeoJSONWriter: cGeoJSONWriter, + } + c.ref() + runtime.AddCleanup(geoJSONWriter, c.destroyGeoJSONWriter, cGeoJSONWriter) + return geoJSONWriter +} + +// WriteGeometry returns the GeoJSON representation of g. +func (w *GeoJSONWriter) WriteGeometry(g *Geom, indent int) string { + w.context.mutex.Lock() + defer w.context.mutex.Unlock() + cGeoJSONStr := C.GEOSGeoJSONWriter_writeGeometry_r(w.context.cHandle, w.cGeoJSONWriter, g.cGeom, C.int(indent)) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer(cGeoJSONStr)) + return C.GoString(cGeoJSONStr) +} + +func (c *Context) destroyGeoJSONWriter(cGeoJSONWriter *C.struct_GEOSGeoJSONWriter_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSGeoJSONWriter_destroy_r(c.cHandle, cGeoJSONWriter) + c.unref() +} diff --git a/vendor/github.com/twpayne/go-geos/geom.go b/vendor/github.com/twpayne/go-geos/geom.go index abe22341..db0743e4 100644 --- a/vendor/github.com/twpayne/go-geos/geom.go +++ b/vendor/github.com/twpayne/go-geos/geom.go @@ -1,4 +1,4 @@ -//go:generate go run ./internal/cmds/execute-template -data geommethods.yaml -output geommethods.go geommethods.go.tmpl +//go:generate go tool execute-template -data geommethods.yaml -output geommethods.go geommethods.go.tmpl package geos @@ -7,126 +7,271 @@ package geos import "C" import ( + "runtime" "unsafe" ) // A Geom is a geometry. type Geom struct { context *Context - geom *C.struct_GEOSGeom_t - parent *Geom + cGeom *C.struct_GEOSGeom_t + owner *Geom typeID TypeID numGeometries int numInteriorRings int numPoints int } -// Destroy destroys g and releases all resources it holds. -func (g *Geom) Destroy() { - // Protect against Destroy being called more than once. - if g == nil || g.context == nil { - return +// NewCollection returns a new collection. +func (c *Context) NewCollection(typeID TypeID, geoms []*Geom) *Geom { + if len(geoms) == 0 { + return c.NewEmptyCollection(typeID) } - if g.parent == nil { - g.context.Lock() - defer g.context.Unlock() - C.GEOSGeom_destroy_r(g.context.handle, g.geom) + c.mutex.Lock() + defer c.mutex.Unlock() + cGeoms := make([]*C.GEOSGeometry, len(geoms)) + for i, geom := range geoms { + cGeoms[i] = C.GEOSGeom_clone_r(c.cHandle, geom.cGeom) } - *g = Geom{} // Clear all references. + geom := c.newNonNilGeom(C.GEOSGeom_createCollection_r(c.cHandle, C.int(typeID), &cGeoms[0], C.uint(len(geoms))), nil) + for _, childGeom := range geoms { + childGeom.owner = geom + } + return geom +} + +// NewEmptyCollection returns a new empty collection. +func (c *Context) NewEmptyCollection(typeID TypeID) *Geom { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.newNonNilGeom(C.GEOSGeom_createEmptyCollection_r(c.cHandle, C.int(typeID)), nil) +} + +// NewEmptyLineString returns a new empty line string. +func (c *Context) NewEmptyLineString() *Geom { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.newNonNilGeom(C.GEOSGeom_createEmptyLineString_r(c.cHandle), nil) +} + +// NewEmptyPoint returns a new empty point. +func (c *Context) NewEmptyPoint() *Geom { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.newNonNilGeom(C.GEOSGeom_createEmptyPoint_r(c.cHandle), nil) +} + +// NewEmptyPolygon returns a new empty polygon. +func (c *Context) NewEmptyPolygon() *Geom { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.newNonNilGeom(C.GEOSGeom_createEmptyPolygon_r(c.cHandle), nil) +} + +// NewGeomFromBounds returns a new polygon constructed from bounds. +func (c *Context) NewGeomFromBounds(minX, minY, maxX, maxY float64) *Geom { + var typeID C.int + cGeom := C.c_newGEOSGeomFromBounds_r(c.cHandle, &typeID, C.double(minX), C.double(minY), C.double(maxX), C.double(maxY)) + if cGeom == nil { + panic(c.err) + } + geom := &Geom{ + context: c, + cGeom: cGeom, + typeID: TypeID(typeID), + numGeometries: 1, + } + c.ref() + runtime.AddCleanup(geom, c.destroyGeom, cGeom) + return geom +} + +// NewLinearRing returns a new linear ring populated with coords. +func (c *Context) NewLinearRing(coords [][]float64) *Geom { + c.mutex.Lock() + defer c.mutex.Unlock() + cCoordSeq := c.newGEOSCoordSeqFromCoords(coords) + return c.newNonNilGeom(C.GEOSGeom_createLinearRing_r(c.cHandle, cCoordSeq), nil) +} + +// NewLineString returns a new line string populated with coords. +func (c *Context) NewLineString(coords [][]float64) *Geom { + c.mutex.Lock() + defer c.mutex.Unlock() + cCoordSeq := c.newGEOSCoordSeqFromCoords(coords) + return c.newNonNilGeom(C.GEOSGeom_createLineString_r(c.cHandle, cCoordSeq), nil) +} + +// NewPoint returns a new point populated with coord. +func (c *Context) NewPoint(coord []float64) *Geom { + cCoordSeq := c.newGEOSCoordSeqFromCoords([][]float64{coord}) + return c.newNonNilGeom(C.GEOSGeom_createPoint_r(c.cHandle, cCoordSeq), nil) +} + +// NewPointFromXY returns a new point with a x and y. +func (c *Context) NewPointFromXY(x, y float64) *Geom { + return c.newNonNilGeom(C.GEOSGeom_createPointFromXY_r(c.cHandle, C.double(x), C.double(y)), nil) +} + +// NewPoints returns a new slice of points populated from coords. +func (c *Context) NewPoints(coords [][]float64) []*Geom { + if coords == nil { + return nil + } + geoms := make([]*Geom, len(coords)) + for i := range geoms { + geoms[i] = c.NewPoint(coords[i]) + } + return geoms +} + +// NewPolygon returns a new polygon populated with coordss. +func (c *Context) NewPolygon(coordss [][][]float64) *Geom { + if len(coordss) == 0 { + return c.NewEmptyPolygon() + } + var ( + cShellGeom *C.struct_GEOSGeom_t + holeCGeoms []*C.struct_GEOSGeom_t + ) + defer func() { + if v := recover(); v != nil { + C.GEOSGeom_destroy_r(c.cHandle, cShellGeom) + for _, cHoleGeom := range holeCGeoms { + C.GEOSGeom_destroy_r(c.cHandle, cHoleGeom) + } + panic(v) + } + }() + cShellGeom = C.GEOSGeom_createLinearRing_r(c.cHandle, c.newGEOSCoordSeqFromCoords(coordss[0])) + if cShellGeom == nil { + panic(c.err) + } + var holeGeoms **C.struct_GEOSGeom_t + nholes := len(coordss) - 1 + if nholes > 0 { + holeCGeoms = make([]*C.struct_GEOSGeom_t, nholes) + for i := range holeCGeoms { + cHoleGeom := C.GEOSGeom_createLinearRing_r(c.cHandle, c.newGEOSCoordSeqFromCoords(coordss[i+1])) + if cHoleGeom == nil { + panic(c.err) + } + holeCGeoms[i] = cHoleGeom + } + holeGeoms = (**C.struct_GEOSGeom_t)(unsafe.Pointer(&holeCGeoms[0])) + } + return c.newNonNilGeom(C.GEOSGeom_createPolygon_r(c.cHandle, cShellGeom, holeGeoms, C.uint(nholes)), nil) } // Bounds returns g's bounds. -func (g *Geom) Bounds() *Bounds { - g.mustNotBeDestroyed() - bounds := NewBoundsEmpty() - g.context.Lock() - defer g.context.Unlock() - C.c_GEOSGeomBounds_r(g.context.handle, g.geom, (*C.double)(&bounds.MinX), (*C.double)(&bounds.MinY), (*C.double)(&bounds.MaxX), (*C.double)(&bounds.MaxY)) +func (g *Geom) Bounds() *Box2D { + bounds := NewBox2DEmpty() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + C.c_GEOSGeomBounds_r(g.context.cHandle, g.cGeom, (*C.double)(&bounds.MinX), (*C.double)(&bounds.MinY), (*C.double)(&bounds.MaxX), (*C.double)(&bounds.MaxY)) return bounds } -// CoordSeq returns g's coordinate sequence. +// MakeValidWithParams returns a new valid geometry using the MakeValidMethods +// and MakeValidCollapsed parameters. +func (g *Geom) MakeValidWithParams(method MakeValidMethod, collapse MakeValidCollapsed) *Geom { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + cRes := C.c_GEOSMakeValidWithParams_r(g.context.cHandle, g.cGeom, C.enum_GEOSMakeValidMethods(method), C.int(collapse)) + return g.context.newGeom(cRes, nil) +} + +// BufferWithParams returns g buffered with bufParams. +func (g *Geom) BufferWithParams(bufParams *BufParams, width float64) *Geom { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + if bufParams.context != g.context { + bufParams.context.mutex.Lock() + defer bufParams.context.mutex.Unlock() + } + return g.context.newNonNilGeom(C.GEOSBufferWithParams_r(g.context.cHandle, g.cGeom, bufParams.cBufParams, C.double(width)), nil) +} + +// ClipByBox2D clips g by box2d. +func (g *Geom) ClipByBox2D(box2d *Box2D) *Geom { + return g.ClipByRect(box2d.MinX, box2d.MinY, box2d.MaxX, box2d.MaxY) +} + +// CoordSeq returns g's coordinate sequence. The returned CoordSeq is owned by g +// and is only valid for as long as g exists. func (g *Geom) CoordSeq() *CoordSeq { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - s := C.GEOSGeom_getCoordSeq_r(g.context.handle, g.geom) - // Don't set a finalizer as coordSeq is owned by g and will be finalized when g is - // finalized. - coordSeq := g.context.newCoordSeq(s, nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + cCoordSeq := C.GEOSGeom_getCoordSeq_r(g.context.cHandle, g.cGeom) + // Don't add a cleanup function as coordSeq is owned by g and will be + // cleaned up when g is cleaned up. + coordSeq := g.context.newCoordSeqInternal(cCoordSeq, g) if coordSeq == nil { return nil } - coordSeq.parent = g + coordSeq.owner = g return coordSeq } -// ExteriorRing returns the exterior ring. +// ExteriorRing returns the exterior ring. The returned geometry is owned by g +// and is only valid for as long as g exists. func (g *Geom) ExteriorRing() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSGetExteriorRing_r(g.context.handle, g.geom), g) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSGetExteriorRing_r(g.context.cHandle, g.cGeom), g) } -// Geometry returns the nth geometry of g. +// Geometry returns the nth geometry of g. The returned geometry is owned by g +// and is only valid for as long as g exists. func (g *Geom) Geometry(n int) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() if n < 0 || g.numGeometries <= n { panic(errIndexOutOfRange) } - return g.context.newNonNilGeom(C.GEOSGetGeometryN_r(g.context.handle, g.geom, C.int(n)), g) + return g.context.newNonNilGeom(C.GEOSGetGeometryN_r(g.context.cHandle, g.cGeom, C.int(n)), g) } -// InteriorRing returns the nth interior ring. +// InteriorRing returns the nth interior ring. The returned geometry is owned by +// g and is only valid for as long as g exists. func (g *Geom) InteriorRing(n int) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() if n < 0 || g.numInteriorRings <= n { panic(errIndexOutOfRange) } - return g.context.newNonNilGeom(C.GEOSGetInteriorRingN_r(g.context.handle, g.geom, C.int(n)), g) + return g.context.newNonNilGeom(C.GEOSGetInteriorRingN_r(g.context.cHandle, g.cGeom, C.int(n)), g) } // IsValidReason returns the reason that g is invalid. func (g *Geom) IsValidReason() string { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - reason := C.GEOSisValidReason_r(g.context.handle, g.geom) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + reason := C.GEOSisValidReason_r(g.context.cHandle, g.cGeom) if reason == nil { panic(g.context.err) } - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer(reason)) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer(reason)) return C.GoString(reason) } // NearestPoints returns the nearest coordinates of g and other. If the nearest // coordinates do not exist (e.g., when either geom is empty), it returns nil. func (g *Geom) NearestPoints(other *Geom) [][]float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - s := C.GEOSNearestPoints_r(g.context.handle, g.geom, other.geom) - if s == nil { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + cCoordSeq := C.GEOSNearestPoints_r(g.context.cHandle, g.cGeom, other.cGeom) + if cCoordSeq == nil { return nil } - defer C.GEOSCoordSeq_destroy_r(g.context.handle, s) - return g.context.newCoordsFromGEOSCoordSeq(s) + defer C.GEOSCoordSeq_destroy_r(g.context.cHandle, cCoordSeq) + return g.context.newCoordsFromGEOSCoordSeq(cCoordSeq) } func (g *Geom) Normalize() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if C.GEOSNormalize_r(g.context.handle, g.geom) != 0 { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + if C.GEOSNormalize_r(g.context.cHandle, g.cGeom) != 0 { panic(g.context.err) } return g @@ -134,10 +279,9 @@ func (g *Geom) Normalize() *Geom { // NumCoordinates returns the number of coordinates in g. func (g *Geom) NumCoordinates() int { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - numCoordinates := C.GEOSGetNumCoordinates_r(g.context.handle, g.geom) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + numCoordinates := C.GEOSGetNumCoordinates_r(g.context.cHandle, g.cGeom) if numCoordinates == -1 { panic(g.context.err) } @@ -146,68 +290,58 @@ func (g *Geom) NumCoordinates() int { // NumGeometries returns the number of geometries in g. func (g *Geom) NumGeometries() int { - g.mustNotBeDestroyed() return g.numGeometries } // NumInteriorRings returns the number of interior rings in g. func (g *Geom) NumInteriorRings() int { - g.mustNotBeDestroyed() return g.numInteriorRings } // NumPoints returns the number of points in g. func (g *Geom) NumPoints() int { - g.mustNotBeDestroyed() return g.numPoints } -// Point returns the g's nth point. +// Point returns the g's nth point. The returned geometry is owned by g and is +// only valid for as long as g exists. func (g *Geom) Point(n int) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() if n < 0 || g.numPoints <= n { panic(errIndexOutOfRange) } - return g.context.newNonNilGeom(C.GEOSGeomGetPointN_r(g.context.handle, g.geom, C.int(n)), nil) + return g.context.newNonNilGeom(C.GEOSGeomGetPointN_r(g.context.cHandle, g.cGeom, C.int(n)), nil) } // PolygonizeFull returns a set of geometries which contains linework that // represents the edge of a planar graph. func (g *Geom) PolygonizeFull() (geom, cuts, dangles, invalidRings *Geom) { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var cCuts, cDangles, cInvalidRings *C.struct_GEOSGeom_t - cGeom := C.GEOSPolygonize_full_r(g.context.handle, g.geom, &cCuts, &cDangles, &cInvalidRings) //nolint:gocritic + cGeom := C.GEOSPolygonize_full_r(g.context.cHandle, g.cGeom, &cCuts, &cDangles, &cInvalidRings) //nolint:gocritic geom = g.context.newNonNilGeom(cGeom, nil) cuts = g.context.newGeom(cCuts, nil) dangles = g.context.newGeom(cDangles, nil) invalidRings = g.context.newGeom(cInvalidRings, nil) - return + return geom, cuts, dangles, invalidRings } // Precision returns g's precision. func (g *Geom) Precision() float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return float64(C.GEOSGeom_getPrecision_r(g.context.handle, g.geom)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return float64(C.GEOSGeom_getPrecision_r(g.context.cHandle, g.cGeom)) } // RelatePattern returns if the DE9IM pattern for g and other matches pat. func (g *Geom) RelatePattern(other *Geom, pat string) bool { - g.mustNotBeDestroyed() patCStr := C.CString(pat) defer C.free(unsafe.Pointer(patCStr)) - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSRelatePattern_r(g.context.handle, g.geom, other.geom, patCStr) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSRelatePattern_r(g.context.cHandle, g.cGeom, other.cGeom, patCStr) { case 0: return false case 1: @@ -219,10 +353,9 @@ func (g *Geom) RelatePattern(other *Geom, pat string) bool { // SRID returns g's SRID. func (g *Geom) SRID() int { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - srid := C.GEOSGetSRID_r(g.context.handle, g.geom) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + srid := C.GEOSGetSRID_r(g.context.cHandle, g.cGeom) // geos_c.h states that GEOSGetSRID_r "Return 0 on exception" but 0 is also // returned if the SRID is not set, so we can't rely on it to propagate // exceptions. @@ -231,107 +364,108 @@ func (g *Geom) SRID() int { // SetSRID sets g's SRID to srid. func (g *Geom) SetSRID(srid int) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - C.GEOSSetSRID_r(g.context.handle, g.geom, C.int(srid)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + C.GEOSSetSRID_r(g.context.cHandle, g.cGeom, C.int(srid)) return g } // SetUserData sets g's userdata and returns g. func (g *Geom) SetUserData(userdata uintptr) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - C.c_GEOSGeom_setUserData_r(g.context.handle, g.geom, C.uintptr_t(userdata)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + C.c_GEOSGeom_setUserData_r(g.context.cHandle, g.cGeom, C.uintptr_t(userdata)) return g } // String returns g in WKT format. func (g *Geom) String() string { - g.mustNotBeDestroyed() return g.ToWKT() } +// ToEWKBWithSRID returns g in Extended WKB format with its SRID. +func (g *Geom) ToEWKBWithSRID() []byte { + return g.context.ewkbWithSRIDWriter().Write(g) +} + // ToGeoJSON returns g in GeoJSON format. func (g *Geom) ToGeoJSON(indent int) string { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if g.context.geoJSONWriter == nil { - g.context.geoJSONWriter = C.GEOSGeoJSONWriter_create_r(g.context.handle) - } - geoJSONCStr := C.GEOSGeoJSONWriter_writeGeometry_r(g.context.handle, g.context.geoJSONWriter, g.geom, C.int(indent)) - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer(geoJSONCStr)) - return C.GoString(geoJSONCStr) + return g.context.geoJSONWriter().WriteGeometry(g, indent) } // ToWKB returns g in WKB format. func (g *Geom) ToWKB() []byte { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if g.context.wkbWriter == nil { - g.context.wkbWriter = C.GEOSWKBWriter_create_r(g.context.handle) - } - var size C.size_t - wkbCBuf := C.GEOSWKBWriter_write_r(g.context.handle, g.context.wkbWriter, g.geom, &size) - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer(wkbCBuf)) - return C.GoBytes(unsafe.Pointer(wkbCBuf), C.int(size)) + return g.context.wkbWriter().Write(g) } // ToWKT returns g in WKT format. func (g *Geom) ToWKT() string { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if g.context.wktWriter == nil { - g.context.wktWriter = C.GEOSWKTWriter_create_r(g.context.handle) - } - wktCStr := C.GEOSWKTWriter_write_r(g.context.handle, g.context.wktWriter, g.geom) - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer(wktCStr)) - return C.GoString(wktCStr) + return g.context.wktWriter().Write(g) } // Type returns g's type. func (g *Geom) Type() string { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - typeCStr := C.GEOSGeomType_r(g.context.handle, g.geom) - if typeCStr == nil { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + cTypeStr := C.GEOSGeomType_r(g.context.cHandle, g.cGeom) + if cTypeStr == nil { panic(g.context.err) } - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer(typeCStr)) - return C.GoString(typeCStr) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer(cTypeStr)) + return C.GoString(cTypeStr) } // TypeID returns g's geometry type id. func (g *Geom) TypeID() TypeID { - g.mustNotBeDestroyed() return g.typeID } // UserData returns g's userdata. func (g *Geom) UserData() uintptr { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return uintptr(C.c_GEOSGeom_getUserData_r(g.context.handle, g.geom)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return uintptr(C.c_GEOSGeom_getUserData_r(g.context.cHandle, g.cGeom)) } -func (g *Geom) finalize() { - if g.context == nil { - return +func (c *Context) newGeom(cGeom *C.struct_GEOSGeom_t, owner *Geom) *Geom { + if cGeom == nil { + return nil + } + var ( + typeID C.int + numGeometries C.int + numPoints C.int + numInteriorRings C.int + ) + if C.c_GEOSGeomGetInfo_r(c.cHandle, cGeom, &typeID, &numGeometries, &numPoints, &numInteriorRings) == 0 { + panic(c.err) } - if g.context.geomFinalizeFunc != nil { - g.context.geomFinalizeFunc(g) + geom := &Geom{ + context: c, + cGeom: cGeom, + owner: owner, + typeID: TypeID(typeID), + numGeometries: int(numGeometries), + numInteriorRings: int(numInteriorRings), + numPoints: int(numPoints), } - g.Destroy() + if owner == nil { + c.ref() + runtime.AddCleanup(geom, c.destroyGeom, cGeom) + } + return geom } -func (g *Geom) mustNotBeDestroyed() { - if g.context == nil { - panic("destroyed Geom") +func (c *Context) newNonNilGeom(cGeom *C.struct_GEOSGeom_t, owner *Geom) *Geom { + if cGeom == nil { + panic(c.err) } + return c.newGeom(cGeom, owner) +} + +func (c *Context) destroyGeom(cGeom *C.struct_GEOSGeom_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSGeom_destroy_r(c.cHandle, cGeom) + c.unref() } diff --git a/vendor/github.com/twpayne/go-geos/geommethods.go b/vendor/github.com/twpayne/go-geos/geommethods.go index d0a3ca12..c57d444a 100644 --- a/vendor/github.com/twpayne/go-geos/geommethods.go +++ b/vendor/github.com/twpayne/go-geos/geommethods.go @@ -9,11 +9,10 @@ import "unsafe" // Area returns g's area. func (g *Geom) Area() float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var area float64 - if C.GEOSArea_r(g.context.handle, g.geom, (*C.double)(&area)) == 0 { + if C.GEOSArea_r(g.context.cHandle, g.cGeom, (*C.double)(&area)) == 0 { panic(g.context.err) } return area @@ -21,86 +20,79 @@ func (g *Geom) Area() float64 { // Boundary returns the boundary of g. func (g *Geom) Boundary() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSBoundary_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSBoundary_r(g.context.cHandle, g.cGeom), nil) } // Buffer returns g with the given buffer. func (g *Geom) Buffer(width float64, quadsegs int) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSBuffer_r(g.context.handle, g.geom, C.double(width), C.int(quadsegs)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSBuffer_r(g.context.cHandle, g.cGeom, C.double(width), C.int(quadsegs)), nil) } // BufferWithStyle returns a buffer using the provided style parameters. func (g *Geom) BufferWithStyle(width float64, quadsegs int, endCapStyle BufCapStyle, joinStyle BufJoinStyle, mitreLimit float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSBufferWithStyle_r(g.context.handle, g.geom, C.double(width), C.int(quadsegs), C.int(endCapStyle), C.int(joinStyle), C.double(mitreLimit)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSBufferWithStyle_r(g.context.cHandle, g.cGeom, C.double(width), C.int(quadsegs), C.int(endCapStyle), C.int(joinStyle), C.double(mitreLimit)), nil) } // BuildArea returns the polygonization using all the linework, assuming that rings contained within rings are empty holes, rather than extra PolygonHoleSimplify. func (g *Geom) BuildArea() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSBuildArea_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSBuildArea_r(g.context.cHandle, g.cGeom), nil) } // Centroid returns a point at the center of mass of g. func (g *Geom) Centroid() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSGetCentroid_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSGetCentroid_r(g.context.cHandle, g.cGeom), nil) } // ClipByRect returns g clipped to a rectangular polygon. -func (g *Geom) ClipByRect(xMin float64, yMin float64, xMax float64, yMax float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSClipByRect_r(g.context.handle, g.geom, C.double(xMin), C.double(yMin), C.double(xMax), C.double(yMax)), nil) +func (g *Geom) ClipByRect(minX float64, minY float64, maxX float64, maxY float64) *Geom { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSClipByRect_r(g.context.cHandle, g.cGeom, C.double(minX), C.double(minY), C.double(maxX), C.double(maxY)), nil) } // Clone returns a clone of g. func (g *Geom) Clone() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSGeom_clone_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSGeom_clone_r(g.context.cHandle, g.cGeom), nil) } // ConcaveHull returns the concave hull of g. func (g *Geom) ConcaveHull(ratio float64, allowHoles uint) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSConcaveHull_r(g.context.handle, g.geom, C.double(ratio), C.unsigned(allowHoles)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSConcaveHull_r(g.context.cHandle, g.cGeom, C.double(ratio), C.unsigned(allowHoles)), nil) +} + +// ConcaveHullByLength returns the concave hull of g. +func (g *Geom) ConcaveHullByLength(ratio float64, allowHoles uint) *Geom { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSConcaveHullByLength_r(g.context.cHandle, g.cGeom, C.double(ratio), C.unsigned(allowHoles)), nil) } // ConstrainedDelaunayTriangulation returns the constrained Delaunay triangulation of the vertices of the g. func (g *Geom) ConstrainedDelaunayTriangulation() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSConstrainedDelaunayTriangulation_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSConstrainedDelaunayTriangulation_r(g.context.cHandle, g.cGeom), nil) } // Contains returns true if g contains other. func (g *Geom) Contains(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSContains_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSContains_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -112,30 +104,23 @@ func (g *Geom) Contains(other *Geom) bool { // ConvexHull returns g's convex hull. func (g *Geom) ConvexHull() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSConvexHull_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSConvexHull_r(g.context.cHandle, g.cGeom), nil) } // CoverageUnion returns the union of g for polygonal inputs that are correctly noded and do not overlap. func (g *Geom) CoverageUnion() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSCoverageUnion_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSCoverageUnion_r(g.context.cHandle, g.cGeom), nil) } // CoveredBy returns true if g is covered by other. func (g *Geom) CoveredBy(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSCoveredBy_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSCoveredBy_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -147,14 +132,9 @@ func (g *Geom) CoveredBy(other *Geom) bool { // Covers returns true if g covers other. func (g *Geom) Covers(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSCovers_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSCovers_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -166,14 +146,9 @@ func (g *Geom) Covers(other *Geom) bool { // Crosses returns true if g crosses other. func (g *Geom) Crosses(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSCrosses_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSCrosses_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -185,46 +160,30 @@ func (g *Geom) Crosses(other *Geom) bool { // Densify returns g densified with the given tolerance. func (g *Geom) Densify(tolerance float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSDensify_r(g.context.handle, g.geom, C.double(tolerance)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSDensify_r(g.context.cHandle, g.cGeom, C.double(tolerance)), nil) } // Difference returns the difference between g and other. func (g *Geom) Difference(other *Geom) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSDifference_r(g.context.handle, g.geom, other.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSDifference_r(g.context.cHandle, g.cGeom, other.cGeom), nil) } // DifferencePrec returns the difference between g and other. func (g *Geom) DifferencePrec(other *Geom, gridSize float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSDifferencePrec_r(g.context.handle, g.geom, other.geom, C.double(gridSize)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSDifferencePrec_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(gridSize)), nil) } // Disjoint returns true if g is disjoint from other. func (g *Geom) Disjoint(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSDisjoint_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSDisjoint_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -234,17 +193,19 @@ func (g *Geom) Disjoint(other *Geom) bool { } } +// DisjointSubsetUnion returns the union of all components of a single geometry (optimized for inputs that can be divided into subsets that do not intersect). +func (g *Geom) DisjointSubsetUnion() *Geom { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSDisjointSubsetUnion_r(g.context.cHandle, g.cGeom), nil) +} + // Distance returns the distance between the closes points on g and other. func (g *Geom) Distance(other *Geom) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var distance float64 - if C.GEOSDistance_r(g.context.handle, g.geom, other.geom, (*C.double)(&distance)) == 0 { + if C.GEOSDistance_r(g.context.cHandle, g.cGeom, other.cGeom, (*C.double)(&distance)) == 0 { panic(g.context.err) } return distance @@ -252,15 +213,10 @@ func (g *Geom) Distance(other *Geom) float64 { // DistanceIndexed returns the distance between g and other, using the indexed facet distance. func (g *Geom) DistanceIndexed(other *Geom) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var distanceIndexed float64 - if C.GEOSDistanceIndexed_r(g.context.handle, g.geom, other.geom, (*C.double)(&distanceIndexed)) == 0 { + if C.GEOSDistanceIndexed_r(g.context.cHandle, g.cGeom, other.cGeom, (*C.double)(&distanceIndexed)) == 0 { panic(g.context.err) } return distanceIndexed @@ -268,14 +224,9 @@ func (g *Geom) DistanceIndexed(other *Geom) float64 { // DistanceWithin returns whether the distance between g and other is within the given dist. func (g *Geom) DistanceWithin(other *Geom, dist float64) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSDistanceWithin_r(g.context.handle, g.geom, other.geom, C.double(dist)) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSDistanceWithin_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(dist)) { case 0: return false case 1: @@ -287,30 +238,23 @@ func (g *Geom) DistanceWithin(other *Geom, dist float64) bool { // EndPoint returns the last point of a LineString. func (g *Geom) EndPoint() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSGeomGetEndPoint_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSGeomGetEndPoint_r(g.context.cHandle, g.cGeom), nil) } // Envelope returns the envelope of g. func (g *Geom) Envelope() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSEnvelope_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSEnvelope_r(g.context.cHandle, g.cGeom), nil) } // Equals returns true if g equals other. func (g *Geom) Equals(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSEquals_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSEquals_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -322,14 +266,9 @@ func (g *Geom) Equals(other *Geom) bool { // EqualsExact returns true if g equals other exactly. func (g *Geom) EqualsExact(other *Geom, tolerance float64) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSEqualsExact_r(g.context.handle, g.geom, other.geom, C.double(tolerance)) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSEqualsExact_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(tolerance)) { case 0: return false case 1: @@ -341,15 +280,10 @@ func (g *Geom) EqualsExact(other *Geom, tolerance float64) bool { // FrechetDistance returns the Fréchet distance between g and other. func (g *Geom) FrechetDistance(other *Geom) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var frechetDistance float64 - if C.GEOSFrechetDistance_r(g.context.handle, g.geom, other.geom, (*C.double)(&frechetDistance)) == 0 { + if C.GEOSFrechetDistance_r(g.context.cHandle, g.cGeom, other.cGeom, (*C.double)(&frechetDistance)) == 0 { panic(g.context.err) } return frechetDistance @@ -357,15 +291,10 @@ func (g *Geom) FrechetDistance(other *Geom) float64 { // FrechetDistanceDensify returns the Fréchet distance between g and other. func (g *Geom) FrechetDistanceDensify(other *Geom, densifyFrac float64) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var frechetDistanceDensify float64 - if C.GEOSFrechetDistanceDensify_r(g.context.handle, g.geom, other.geom, C.double(densifyFrac), (*C.double)(&frechetDistanceDensify)) == 0 { + if C.GEOSFrechetDistanceDensify_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(densifyFrac), (*C.double)(&frechetDistanceDensify)) == 0 { panic(g.context.err) } return frechetDistanceDensify @@ -373,10 +302,9 @@ func (g *Geom) FrechetDistanceDensify(other *Geom, densifyFrac float64) float64 // HasZ returns if g has Z coordinates. func (g *Geom) HasZ() bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - switch C.GEOSHasZ_r(g.context.handle, g.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSHasZ_r(g.context.cHandle, g.cGeom) { case 0: return false case 1: @@ -388,15 +316,10 @@ func (g *Geom) HasZ() bool { // HausdorffDistance returns the Hausdorff distance between g and other. func (g *Geom) HausdorffDistance(other *Geom) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var hausdorffDistance float64 - if C.GEOSHausdorffDistance_r(g.context.handle, g.geom, other.geom, (*C.double)(&hausdorffDistance)) == 0 { + if C.GEOSHausdorffDistance_r(g.context.cHandle, g.cGeom, other.cGeom, (*C.double)(&hausdorffDistance)) == 0 { panic(g.context.err) } return hausdorffDistance @@ -404,15 +327,10 @@ func (g *Geom) HausdorffDistance(other *Geom) float64 { // HausdorffDistanceDensify returns the Hausdorff distance between g and other. func (g *Geom) HausdorffDistanceDensify(other *Geom, densifyFrac float64) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var hausdorffDistanceDensify float64 - if C.GEOSHausdorffDistanceDensify_r(g.context.handle, g.geom, other.geom, C.double(densifyFrac), (*C.double)(&hausdorffDistanceDensify)) == 0 { + if C.GEOSHausdorffDistanceDensify_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(densifyFrac), (*C.double)(&hausdorffDistanceDensify)) == 0 { panic(g.context.err) } return hausdorffDistanceDensify @@ -420,54 +338,37 @@ func (g *Geom) HausdorffDistanceDensify(other *Geom, densifyFrac float64) float6 // Interpolate returns a point distance d from the start of g, which must be a linestring. func (g *Geom) Interpolate(d float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newGeom(C.GEOSInterpolate_r(g.context.handle, g.geom, C.double(d)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSInterpolate_r(g.context.cHandle, g.cGeom, C.double(d)), nil) } // InterpolateNormalized returns the point that is at proportion from the start. func (g *Geom) InterpolateNormalized(proportion float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newGeom(C.GEOSInterpolateNormalized_r(g.context.handle, g.geom, C.double(proportion)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSInterpolateNormalized_r(g.context.cHandle, g.cGeom, C.double(proportion)), nil) } // Intersection returns the intersection of g and other. func (g *Geom) Intersection(other *Geom) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSIntersection_r(g.context.handle, g.geom, other.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSIntersection_r(g.context.cHandle, g.cGeom, other.cGeom), nil) } // IntersectionPrec returns the intersection of g and other. func (g *Geom) IntersectionPrec(other *Geom, gridSize float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSIntersectionPrec_r(g.context.handle, g.geom, other.geom, C.double(gridSize)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSIntersectionPrec_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(gridSize)), nil) } // Intersects returns true if g intersects other. func (g *Geom) Intersects(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSIntersects_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSIntersects_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -479,10 +380,9 @@ func (g *Geom) Intersects(other *Geom) bool { // IsClosed returns true if g is closed. func (g *Geom) IsClosed() bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - switch C.GEOSisClosed_r(g.context.handle, g.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSisClosed_r(g.context.cHandle, g.cGeom) { case 0: return false case 1: @@ -494,10 +394,9 @@ func (g *Geom) IsClosed() bool { // IsEmpty returns true if g is empty. func (g *Geom) IsEmpty() bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - switch C.GEOSisEmpty_r(g.context.handle, g.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSisEmpty_r(g.context.cHandle, g.cGeom) { case 0: return false case 1: @@ -509,10 +408,9 @@ func (g *Geom) IsEmpty() bool { // IsRing returns true if g is a ring. func (g *Geom) IsRing() bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - switch C.GEOSisRing_r(g.context.handle, g.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSisRing_r(g.context.cHandle, g.cGeom) { case 0: return false case 1: @@ -524,10 +422,9 @@ func (g *Geom) IsRing() bool { // IsSimple returns true if g is simple. func (g *Geom) IsSimple() bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - switch C.GEOSisSimple_r(g.context.handle, g.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSisSimple_r(g.context.cHandle, g.cGeom) { case 0: return false case 1: @@ -539,10 +436,9 @@ func (g *Geom) IsSimple() bool { // IsValid returns true if g is valid. func (g *Geom) IsValid() bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - switch C.GEOSisValid_r(g.context.handle, g.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSisValid_r(g.context.cHandle, g.cGeom) { case 0: return false case 1: @@ -554,23 +450,17 @@ func (g *Geom) IsValid() bool { // LargestEmptyCircle returns the largest empty circle for g, up to a specified tolerance. func (g *Geom) LargestEmptyCircle(other *Geom, tolerance float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSLargestEmptyCircle_r(g.context.handle, g.geom, other.geom, C.double(tolerance)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSLargestEmptyCircle_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(tolerance)), nil) } // Length returns g's length. func (g *Geom) Length() float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var length float64 - if C.GEOSLength_r(g.context.handle, g.geom, (*C.double)(&length)) == 0 { + if C.GEOSLength_r(g.context.cHandle, g.cGeom, (*C.double)(&length)) == 0 { panic(g.context.err) } return length @@ -578,35 +468,31 @@ func (g *Geom) Length() float64 { // LineMerge returns a set of fully noded LineStrings, removing any cardinality 2 nodes in the linework. func (g *Geom) LineMerge() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSLineMerge_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSLineMerge_r(g.context.cHandle, g.cGeom), nil) } // MakeValid repairs an invalid geometry, returning a valid output. func (g *Geom) MakeValid() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSMakeValid_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSMakeValid_r(g.context.cHandle, g.cGeom), nil) } // MaximumInscribedCircle returns the maximum inscribed circle of g up to the the given tolerance. func (g *Geom) MaximumInscribedCircle(tolerance float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSMaximumInscribedCircle_r(g.context.handle, g.geom, C.double(tolerance)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSMaximumInscribedCircle_r(g.context.cHandle, g.cGeom, C.double(tolerance)), nil) } // MinimumClearance returns the minimum clearance of g. func (g *Geom) MinimumClearance() float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var minimumClearance float64 - if C.GEOSMinimumClearance_r(g.context.handle, g.geom, (*C.double)(&minimumClearance)) == 0 { + if C.GEOSMinimumClearance_r(g.context.cHandle, g.cGeom, (*C.double)(&minimumClearance)) == 0 { panic(g.context.err) } return minimumClearance @@ -614,54 +500,44 @@ func (g *Geom) MinimumClearance() float64 { // MinimumClearanceLine returns a LineString whose endpoints define the minimum clearance of g. func (g *Geom) MinimumClearanceLine() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSMinimumClearanceLine_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSMinimumClearanceLine_r(g.context.cHandle, g.cGeom), nil) } // MinimumRotatedRectangle returns the minimum rotated rectangle enclosing g. func (g *Geom) MinimumRotatedRectangle() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSMinimumRotatedRectangle_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSMinimumRotatedRectangle_r(g.context.cHandle, g.cGeom), nil) } // MinimumWidth returns a linestring geometry which represents the minimum diameter of g. func (g *Geom) MinimumWidth() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSMinimumWidth_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSMinimumWidth_r(g.context.cHandle, g.cGeom), nil) } // Node returns a new geometry in which no lines cross each other, and all touching occurs at endpoints. func (g *Geom) Node() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSNode_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSNode_r(g.context.cHandle, g.cGeom), nil) } // OffsetCurve returns the offset curve line(s) of g. func (g *Geom) OffsetCurve(width float64, quadsegs int, joinStyle BufJoinStyle, mitreLimit float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSOffsetCurve_r(g.context.handle, g.geom, C.double(width), C.int(quadsegs), C.int(joinStyle), C.double(mitreLimit)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSOffsetCurve_r(g.context.cHandle, g.cGeom, C.double(width), C.int(quadsegs), C.int(joinStyle), C.double(mitreLimit)), nil) } // Overlaps returns true if g overlaps other. func (g *Geom) Overlaps(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSOverlaps_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSOverlaps_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -673,162 +549,111 @@ func (g *Geom) Overlaps(other *Geom) bool { // PointOnSurface returns a point that is inside the boundary of a polygonal geometry. func (g *Geom) PointOnSurface() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSPointOnSurface_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSPointOnSurface_r(g.context.cHandle, g.cGeom), nil) } // Project returns the distance of other(a point) projected onto g(a line) from the start of the line. func (g *Geom) Project(other *Geom) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return float64(C.GEOSProject_r(g.context.handle, g.geom, other.geom)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return float64(C.GEOSProject_r(g.context.cHandle, g.cGeom, other.cGeom)) } // ProjectNormalized returns the proportional distance of other(a point) projected onto g(a line) from the start of the line. For example, a point that projects to the middle of a line would be return 0.5. func (g *Geom) ProjectNormalized(other *Geom) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return float64(C.GEOSProjectNormalized_r(g.context.handle, g.geom, other.geom)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return float64(C.GEOSProjectNormalized_r(g.context.cHandle, g.cGeom, other.cGeom)) } // Relate returns the DE9IM pattern for g and other. func (g *Geom) Relate(other *Geom) string { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - relateCStr := C.GEOSRelate_r(g.context.handle, g.geom, other.geom) - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer(relateCStr)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + relateCStr := C.GEOSRelate_r(g.context.cHandle, g.cGeom, other.cGeom) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer(relateCStr)) return C.GoString(relateCStr) } // RelateBoundaryNodeRule returns the DE9IM pattern for g and other. func (g *Geom) RelateBoundaryNodeRule(other *Geom, bnr RelateBoundaryNodeRule) string { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - relateBoundaryNodeRuleCStr := C.GEOSRelateBoundaryNodeRule_r(g.context.handle, g.geom, other.geom, C.int(bnr)) - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer(relateBoundaryNodeRuleCStr)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + relateBoundaryNodeRuleCStr := C.GEOSRelateBoundaryNodeRule_r(g.context.cHandle, g.cGeom, other.cGeom, C.int(bnr)) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer(relateBoundaryNodeRuleCStr)) return C.GoString(relateBoundaryNodeRuleCStr) } // Reverse returns g with sequence orders reversed. func (g *Geom) Reverse() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSReverse_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSReverse_r(g.context.cHandle, g.cGeom), nil) } // SetPrecision changes the coordinate precision of g. func (g *Geom) SetPrecision(gridSize float64, flags PrecisionRule) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSGeom_setPrecision_r(g.context.handle, g.geom, C.double(gridSize), C.int(flags)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSGeom_setPrecision_r(g.context.cHandle, g.cGeom, C.double(gridSize), C.int(flags)), nil) } // SharedPaths returns the paths shared between g and other, which must be lineal geometries. func (g *Geom) SharedPaths(other *Geom) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSSharedPaths_r(g.context.handle, g.geom, other.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSSharedPaths_r(g.context.cHandle, g.cGeom, other.cGeom), nil) } // Simplify returns a simplified geometry. func (g *Geom) Simplify(tolerance float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSSimplify_r(g.context.handle, g.geom, C.double(tolerance)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSSimplify_r(g.context.cHandle, g.cGeom, C.double(tolerance)), nil) } // Snap returns a geometry with the vertices and segments of g snapped to other within the given tolerance. func (g *Geom) Snap(other *Geom, tolerance float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSSnap_r(g.context.handle, g.geom, other.geom, C.double(tolerance)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSSnap_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(tolerance)), nil) } // StartPoint returns the first point of a LineString. func (g *Geom) StartPoint() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSGeomGetStartPoint_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSGeomGetStartPoint_r(g.context.cHandle, g.cGeom), nil) } // SymDifference returns the symmetric difference between g and other. func (g *Geom) SymDifference(other *Geom) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSSymDifference_r(g.context.handle, g.geom, other.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSSymDifference_r(g.context.cHandle, g.cGeom, other.cGeom), nil) } // SymDifferencePrec returns the symmetric difference between g and other. func (g *Geom) SymDifferencePrec(other *Geom, gridSize float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSSymDifferencePrec_r(g.context.handle, g.geom, other.geom, C.double(gridSize)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSSymDifferencePrec_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(gridSize)), nil) } // TopologyPreserveSimplify returns a simplified geometry preserving topology. func (g *Geom) TopologyPreserveSimplify(tolerance float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSTopologyPreserveSimplify_r(g.context.handle, g.geom, C.double(tolerance)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSTopologyPreserveSimplify_r(g.context.cHandle, g.cGeom, C.double(tolerance)), nil) } // Touches returns true if g touches other. func (g *Geom) Touches(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSTouches_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSTouches_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -840,54 +665,37 @@ func (g *Geom) Touches(other *Geom) bool { // UnaryUnion returns the union of all components of a single geometry. func (g *Geom) UnaryUnion() *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSUnaryUnion_r(g.context.handle, g.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSUnaryUnion_r(g.context.cHandle, g.cGeom), nil) } // UnaryUnionPrec returns the union of all components of a single geometry. func (g *Geom) UnaryUnionPrec(gridSize float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.newNonNilGeom(C.GEOSUnaryUnionPrec_r(g.context.handle, g.geom, C.double(gridSize)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newNonNilGeom(C.GEOSUnaryUnionPrec_r(g.context.cHandle, g.cGeom, C.double(gridSize)), nil) } // Union returns the union of g and other. func (g *Geom) Union(other *Geom) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSUnion_r(g.context.handle, g.geom, other.geom), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSUnion_r(g.context.cHandle, g.cGeom, other.cGeom), nil) } // UnionPrec returns the union of g and other. func (g *Geom) UnionPrec(other *Geom, gridSize float64) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.GEOSUnionPrec_r(g.context.handle, g.geom, other.geom, C.double(gridSize)), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.GEOSUnionPrec_r(g.context.cHandle, g.cGeom, other.cGeom, C.double(gridSize)), nil) } // Within returns true if g is within other. func (g *Geom) Within(other *Geom) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.GEOSWithin_r(g.context.handle, g.geom, other.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.GEOSWithin_r(g.context.cHandle, g.cGeom, other.cGeom) { case 0: return false case 1: @@ -899,11 +707,10 @@ func (g *Geom) Within(other *Geom) bool { // X returns g's X coordinate. func (g *Geom) X() float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var x float64 - if C.GEOSGeomGetX_r(g.context.handle, g.geom, (*C.double)(&x)) == 0 { + if C.GEOSGeomGetX_r(g.context.cHandle, g.cGeom, (*C.double)(&x)) == 0 { panic(g.context.err) } return x @@ -911,11 +718,10 @@ func (g *Geom) X() float64 { // Y returns g's Y coordinate. func (g *Geom) Y() float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var y float64 - if C.GEOSGeomGetY_r(g.context.handle, g.geom, (*C.double)(&y)) == 0 { + if C.GEOSGeomGetY_r(g.context.cHandle, g.cGeom, (*C.double)(&y)) == 0 { panic(g.context.err) } return y diff --git a/vendor/github.com/twpayne/go-geos/geommethods.go.tmpl b/vendor/github.com/twpayne/go-geos/geommethods.go.tmpl index d085cd82..5cab0542 100644 --- a/vendor/github.com/twpayne/go-geos/geommethods.go.tmpl +++ b/vendor/github.com/twpayne/go-geos/geommethods.go.tmpl @@ -27,34 +27,27 @@ import "unsafe" {{ if .comment }}// {{ .name }} {{ .comment }}.{{ end }} func (g *Geom) {{ .name }}({{ range $index, $arg := .extraArgs }}{{ if $index }}, {{ end }}{{ $arg.name }} {{ $arg.type }}{{ end }}) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - return g.context.new{{ if not .nil }}NonNil{{ end }}Geom(C.{{ $geosFunction }}(g.context.handle, g.geom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.new{{ if not .nil }}NonNil{{ end }}Geom(C.{{ $geosFunction }}(g.context.cHandle, g.cGeom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}), nil) } {{- else if eq .type "binary" }} {{ if .comment }}// {{ .name }} {{ .comment }}.{{ end }} func (g *Geom) {{ .name }}(other *Geom{{ range .extraArgs }}, {{ .name }} {{ .type }}{{ end }}) *Geom { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - return g.context.newGeom(C.{{ $geosFunction }}(g.context.handle, g.geom, other.geom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}), nil) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + return g.context.newGeom(C.{{ $geosFunction }}(g.context.cHandle, g.cGeom, other.cGeom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}), nil) } {{- else if eq .type "unaryPredicate" }} {{ if .comment }}// {{ .name }} {{ .comment }}.{{ end }} func (g *Geom) {{ .name }}() bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - switch C.{{ $geosFunction }}(g.context.handle, g.geom) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.{{ $geosFunction }}(g.context.cHandle, g.cGeom) { case 0: return false case 1: @@ -68,14 +61,9 @@ func (g *Geom) {{ .name }}() bool { {{ if .comment }}// {{ .name }} {{ .comment }}.{{ end }} func (g *Geom) {{ .name }}(other *Geom{{ range .extraArgs }}, {{ .name }} {{ .type }}{{ end }}) bool { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - switch C.{{ $geosFunction }}(g.context.handle, g.geom, other.geom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}) { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + switch C.{{ $geosFunction }}(g.context.cHandle, g.cGeom, other.cGeom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}) { case 0: return false case 1: @@ -90,11 +78,10 @@ func (g *Geom) {{ .name }}(other *Geom{{ range .extraArgs }}, {{ .name }} {{ .ty {{ if .comment }}// {{ .name }} {{ .comment }}.{{ end }} func (g *Geom) {{ .name }}() float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() + g.context.mutex.Lock() + defer g.context.mutex.Unlock() var {{ $varName }} float64 - if C.{{ $geosFunction }}(g.context.handle, g.geom, (*C.double)(&{{ $varName }})) == 0 { + if C.{{ $geosFunction }}(g.context.cHandle, g.cGeom, (*C.double)(&{{ $varName }})) == 0 { panic(g.context.err) } return {{ $varName }} @@ -105,18 +92,13 @@ func (g *Geom) {{ .name }}() float64 { {{ if .comment }}// {{ .name }} {{ .comment }}.{{ end }} func (g *Geom) {{ .name }}(other *Geom{{ range .extraArgs }}, {{ .name }} {{ .type }}{{ end }}) float64 { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } + g.context.mutex.Lock() + defer g.context.mutex.Unlock() {{- if .valueReturned }} - return float64(C.{{ $geosFunction }}(g.context.handle, g.geom, other.geom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }})) + return float64(C.{{ $geosFunction }}(g.context.cHandle, g.cGeom, other.cGeom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }})) {{- else }} var {{ $varName }} float64 - if C.{{ $geosFunction }}(g.context.handle, g.geom, other.geom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}, (*C.double)(&{{ $varName }})) == 0 { + if C.{{ $geosFunction }}(g.context.cHandle, g.cGeom, other.cGeom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}, (*C.double)(&{{ $varName }})) == 0 { panic(g.context.err) } return {{ $varName }} @@ -128,15 +110,10 @@ func (g *Geom) {{ .name }}(other *Geom{{ range .extraArgs }}, {{ .name }} {{ .ty {{ if .comment }}// {{ .name }} {{ .comment }}.{{ end }} func (g *Geom) {{ .name }}(other *Geom{{ range .extraArgs }}, {{ .name }} {{ .type }}{{ end }}) string { - g.mustNotBeDestroyed() - g.context.Lock() - defer g.context.Unlock() - if other.context != g.context { - other.context.Lock() - defer other.context.Unlock() - } - {{ $varName }}CStr := C.{{ $geosFunction }}(g.context.handle, g.geom, other.geom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}) - defer C.GEOSFree_r(g.context.handle, unsafe.Pointer({{ $varName }}CStr)) + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + {{ $varName }}CStr := C.{{ $geosFunction }}(g.context.cHandle, g.cGeom, other.cGeom{{ range .extraArgs }}, {{ .type | cType }}({{ .name }}){{ end }}) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer({{ $varName }}CStr)) return C.GoString({{ $varName }}CStr) } diff --git a/vendor/github.com/twpayne/go-geos/geommethods.yaml b/vendor/github.com/twpayne/go-geos/geommethods.yaml index 757d8007..a5bf6f43 100644 --- a/vendor/github.com/twpayne/go-geos/geommethods.yaml +++ b/vendor/github.com/twpayne/go-geos/geommethods.yaml @@ -37,13 +37,13 @@ comment: returns g clipped to a rectangular polygon type: unary extraArgs: - - name: xMin + - name: minX type: float64 - - name: yMin + - name: minY type: float64 - - name: xMax + - name: maxX type: float64 - - name: yMax + - name: maxY type: float64 - name: Clone comment: returns a clone of g @@ -57,6 +57,14 @@ type: float64 - name: allowHoles type: uint +- name: ConcaveHullByLength + comment: returns the concave hull of g + type: unary + extraArgs: + - name: ratio + type: float64 + - name: allowHoles + type: uint - name: ConstrainedDelaunayTriangulation comment: returns the constrained Delaunay triangulation of the vertices of the g type: unary @@ -96,6 +104,9 @@ - name: Disjoint comment: returns true if g is disjoint from other type: binaryPredicate +- name: DisjointSubsetUnion + comment: returns the union of all components of a single geometry (optimized for inputs that can be divided into subsets that do not intersect) + type: unary - name: Distance comment: returns the distance between the closes points on g and other type: float64BinaryProperty diff --git a/vendor/github.com/twpayne/go-geos/geos.go b/vendor/github.com/twpayne/go-geos/geos.go index edb9bc99..aa5bcef8 100644 --- a/vendor/github.com/twpayne/go-geos/geos.go +++ b/vendor/github.com/twpayne/go-geos/geos.go @@ -27,7 +27,7 @@ const ( TypeIDGeometryCollection TypeID = C.GEOS_GEOMETRYCOLLECTION ) -// A BoundaryNodeRule is a boundary node rule. +// A RelateBoundaryNodeRule is a relate boundary node rule. type RelateBoundaryNodeRule int // Boundary node rules. @@ -81,3 +81,43 @@ const ( PrecisionRulePointwise PrecisionRule = C.GEOS_PREC_NO_TOPO PrecisionRuleKeepCollapsed PrecisionRule = C.GEOS_PREC_KEEP_COLLAPSED ) + +type MakeValidMethod int + +// MakeValidMethods. +const ( + MakeValidLinework MakeValidMethod = C.GEOS_MAKE_VALID_LINEWORK + MakeValidStructure MakeValidMethod = C.GEOS_MAKE_VALID_STRUCTURE +) + +type MakeValidCollapsed int + +// MakeValidMethods. +const ( + MakeValidDiscardCollapsed MakeValidCollapsed = 0 + MakeValidKeepCollapsed MakeValidCollapsed = 1 +) + +// VersionCompare returns a negative number if the GEOS version is less than the +// given major.minor.patch version, zero if it is equal, or a positive number +// otherwise. +func VersionCompare(major, minor, patch int) int { + if majorDelta := VersionMajor - major; majorDelta != 0 { + return majorDelta + } + if minorDelta := VersionMinor - minor; minorDelta != 0 { + return minorDelta + } + return VersionPatch - patch +} + +type intType interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +func toInt[T intType](b bool) T { //nolint:ireturn + if b { + return 1 + } + return 0 +} diff --git a/vendor/github.com/twpayne/go-geos/go-geos.c b/vendor/github.com/twpayne/go-geos/go-geos.c index 137d7a07..3196ed66 100644 --- a/vendor/github.com/twpayne/go-geos/go-geos.c +++ b/vendor/github.com/twpayne/go-geos/go-geos.c @@ -189,6 +189,24 @@ int c_GEOSSTRtree_distance_callback(const void *item1, const void *item2, return go_GEOSSTRtree_distance_callback(item1, item2, distance, userdata); } +GEOSGeometry *c_GEOSMakeValidWithParams_r(GEOSContextHandle_t handle, + const GEOSGeometry *g, + enum GEOSMakeValidMethods method, + int keepCollapsed) { + GEOSGeometry *res; + GEOSMakeValidParams *par; + + par = GEOSMakeValidParams_create_r(handle); + GEOSMakeValidParams_setKeepCollapsed_r(handle, par, keepCollapsed); + GEOSMakeValidParams_setMethod_r(handle, par, method); + + res = GEOSMakeValidWithParams_r(handle, g, par); + + GEOSMakeValidParams_destroy_r(handle, par); + + return res; +} + #if GEOS_VERSION_MAJOR < 3 || \ (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11) @@ -198,4 +216,32 @@ GEOSGeometry *GEOSConcaveHull_r(GEOSContextHandle_t handle, return NULL; } -#endif \ No newline at end of file +#endif + +#if GEOS_VERSION_MAJOR < 3 || \ + (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12) + +GEOSGeometry *GEOSConcaveHullByLength_r(GEOSContextHandle_t handle, + const GEOSGeometry *g, double ratio, + unsigned int allowHoles) { + return NULL; +} + +char GEOSPreparedContainsXY_r(GEOSContextHandle_t handle, + const GEOSPreparedGeometry *pg1, double x, + double y) { + return 0; +} + +char GEOSPreparedIntersectsXY_r(GEOSContextHandle_t handle, + const GEOSPreparedGeometry *pg1, double x, + double y) { + return 0; +} + +GEOSGeometry *GEOSDisjointSubsetUnion_r(GEOSContextHandle_t handle, + const GEOSGeometry *g) { + return NULL; +} + +#endif diff --git a/vendor/github.com/twpayne/go-geos/go-geos.h b/vendor/github.com/twpayne/go-geos/go-geos.h index 19f79094..f1d3885a 100644 --- a/vendor/github.com/twpayne/go-geos/go-geos.h +++ b/vendor/github.com/twpayne/go-geos/go-geos.h @@ -26,6 +26,10 @@ GEOSGeometry *c_newGEOSGeomFromBounds_r(GEOSContextHandle_t handle, int *typeID, int c_GEOSSTRtree_distance_callback(const void *item1, const void *item2, double *distance, void *userdata); void c_GEOSSTRtree_query_callback(void *elem, void *userdata); +GEOSGeometry *c_GEOSMakeValidWithParams_r(GEOSContextHandle_t handle, + const GEOSGeometry *g, + enum GEOSMakeValidMethods method, + int keepCollapsed); #if GEOS_VERSION_MAJOR < 3 || \ (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11) @@ -36,4 +40,24 @@ GEOSGeometry *GEOSConcaveHull_r(GEOSContextHandle_t handle, #endif -#endif \ No newline at end of file +#if GEOS_VERSION_MAJOR < 3 || \ + (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 12) + +GEOSGeometry *GEOSConcaveHullByLength_r(GEOSContextHandle_t handle, + const GEOSGeometry *g, double ratio, + unsigned int allowHoles); + +char GEOSPreparedContainsXY_r(GEOSContextHandle_t handle, + const GEOSPreparedGeometry *pg1, double x, + double y); + +char GEOSPreparedIntersectsXY_r(GEOSContextHandle_t handle, + const GEOSPreparedGeometry *pg1, double x, + double y); + +GEOSGeometry *GEOSDisjointSubsetUnion_r(GEOSContextHandle_t handle, + const GEOSGeometry *g); + +#endif + +#endif diff --git a/vendor/github.com/twpayne/go-geos/prepgeom.go b/vendor/github.com/twpayne/go-geos/prepgeom.go index cd6f3f73..3cc0e129 100644 --- a/vendor/github.com/twpayne/go-geos/prepgeom.go +++ b/vendor/github.com/twpayne/go-geos/prepgeom.go @@ -7,205 +7,264 @@ import "runtime" // A PrepGeom is a prepared geometry. type PrepGeom struct { - parent *Geom - pgeom *C.struct_GEOSPrepGeom_t + owner *Geom + cPrepGeom *C.struct_GEOSPrepGeom_t } // Prepare prepares g. func (g *Geom) Prepare() *PrepGeom { - g.context.Lock() - defer g.context.Unlock() - pg := &PrepGeom{ - parent: g, - pgeom: C.GEOSPrepare_r(g.context.handle, g.geom), + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + cPrepGeom := C.GEOSPrepare_r(g.context.cHandle, g.cGeom) + prepGeom := &PrepGeom{ + owner: g, + cPrepGeom: cPrepGeom, } - runtime.SetFinalizer(pg, (*PrepGeom).destroy) - return pg + g.context.ref() + runtime.AddCleanup(prepGeom, g.context.destroyPrepGeom, cPrepGeom) + return prepGeom } // Contains returns if pg contains g. func (pg *PrepGeom) Contains(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedContains_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedContains_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } // ContainsProperly returns if pg contains g properly. func (pg *PrepGeom) ContainsProperly(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedContainsProperly_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedContainsProperly_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) + } +} + +// ContainsXY returns if pg contains the point (x, y). +func (pg *PrepGeom) ContainsXY(x, y float64) bool { + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + switch C.GEOSPreparedContainsXY_r(pg.owner.context.cHandle, pg.cPrepGeom, C.double(x), C.double(y)) { + case 0: + return false + case 1: + return true + default: + panic(pg.owner.context.err) } } // CoveredBy returns if pg is covered by g. func (pg *PrepGeom) CoveredBy(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedCoveredBy_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedCoveredBy_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } // Covers returns if pg covers g. func (pg *PrepGeom) Covers(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedCovers_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedCovers_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } // Crosses returns if pg crosses g. func (pg *PrepGeom) Crosses(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedCrosses_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedCrosses_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } // Disjoint returns if pg is disjoint from g. func (pg *PrepGeom) Disjoint(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + } + switch C.GEOSPreparedDisjoint_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { + case 0: + return false + case 1: + return true + default: + panic(pg.owner.context.err) } - switch C.GEOSPreparedDisjoint_r(pg.parent.context.handle, pg.pgeom, g.geom) { +} + +// DistanceWithin returns if pg is within dist g. +func (pg *PrepGeom) DistanceWithin(g *Geom, dist float64) bool { + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + } + switch C.GEOSPreparedDistanceWithin_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom, C.double(dist)) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } // Intersects returns if pg contains g. func (pg *PrepGeom) Intersects(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() + } + switch C.GEOSPreparedIntersects_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { + case 0: + return false + case 1: + return true + default: + panic(pg.owner.context.err) } - switch C.GEOSPreparedIntersects_r(pg.parent.context.handle, pg.pgeom, g.geom) { +} + +// IntersectsXY returns if pg intersects the point at (x, y). +func (pg *PrepGeom) IntersectsXY(x, y float64) bool { + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + switch C.GEOSPreparedIntersectsXY_r(pg.owner.context.cHandle, pg.cPrepGeom, C.double(x), C.double(y)) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) + } +} + +// NearestPoints returns if pg overlaps g. +func (pg *PrepGeom) NearestPoints(g *Geom) *CoordSeq { + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } + return pg.owner.context.newNonNilCoordSeq(C.GEOSPreparedNearestPoints_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom)) } // Overlaps returns if pg overlaps g. func (pg *PrepGeom) Overlaps(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedOverlaps_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedOverlaps_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } // Touches returns if pg contains g. func (pg *PrepGeom) Touches(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedTouches_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedTouches_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } // Within returns if pg is within g. func (pg *PrepGeom) Within(g *Geom) bool { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - if g.context != pg.parent.context { - g.context.Lock() - defer g.context.Unlock() + pg.owner.context.mutex.Lock() + defer pg.owner.context.mutex.Unlock() + if g.context != pg.owner.context { + g.context.mutex.Lock() + defer g.context.mutex.Unlock() } - switch C.GEOSPreparedWithin_r(pg.parent.context.handle, pg.pgeom, g.geom) { + switch C.GEOSPreparedWithin_r(pg.owner.context.cHandle, pg.cPrepGeom, g.cGeom) { case 0: return false case 1: return true default: - panic(pg.parent.context.err) + panic(pg.owner.context.err) } } -func (pg *PrepGeom) destroy() { - pg.parent.context.Lock() - defer pg.parent.context.Unlock() - C.GEOSPreparedGeom_destroy_r(pg.parent.context.handle, pg.pgeom) - *pg = PrepGeom{} // Clear all references. +func (c *Context) destroyPrepGeom(cPrepGeom *C.struct_GEOSPrepGeom_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSPreparedGeom_destroy_r(c.cHandle, cPrepGeom) + c.unref() } diff --git a/vendor/github.com/twpayne/go-geos/strtree.go b/vendor/github.com/twpayne/go-geos/strtree.go index d26c158b..41413685 100644 --- a/vendor/github.com/twpayne/go-geos/strtree.go +++ b/vendor/github.com/twpayne/go-geos/strtree.go @@ -5,30 +5,34 @@ package geos import "C" import ( + "runtime" "runtime/cgo" "unsafe" ) // An STRtree is an R-tree spatial index structure for two dimensional data. +// +// WARNING The Go bindings to STRtree are currently broken. Do not use them. type STRtree struct { - context *Context - strTree *C.struct_GEOSSTRtree_t - itemToValue map[unsafe.Pointer]any - valueToItem map[any]unsafe.Pointer + context *Context + cSTRtree *C.struct_GEOSSTRtree_t + valueHandles map[any]*cgo.Handle } -// Destroy frees all resources associated with t. -func (t *STRtree) Destroy() { - if t == nil || t.context == nil { - return +// NewSTRtree returns a new STRtree. +func (c *Context) NewSTRtree(nodeCapacity int) *STRtree { + c.mutex.Lock() + defer c.mutex.Unlock() + cSTRtree := C.GEOSSTRtree_create_r(c.cHandle, C.size_t(nodeCapacity)) + strTree := &STRtree{ + context: c, + cSTRtree: cSTRtree, + valueHandles: make(map[any]*cgo.Handle), } - t.context.Lock() - defer t.context.Unlock() - C.GEOSSTRtree_destroy_r(t.context.handle, t.strTree) - for item := range t.itemToValue { - C.free(item) - } - *t = STRtree{} // Clear all references. + c.ref() + runtime.AddCleanup(strTree, c.destroySTRtree, cSTRtree) + runtime.AddCleanup(strTree, destoryValueHandles, strTree.valueHandles) + return strTree } // Insert inserts value with geometry g. @@ -36,78 +40,102 @@ func (t *STRtree) Insert(g *Geom, value any) error { if g.context != t.context { panic(errContextMismatch) } - t.context.Lock() - defer t.context.Unlock() - if _, ok := t.valueToItem[value]; ok { + t.context.mutex.Lock() + defer t.context.mutex.Unlock() + if _, ok := t.valueHandles[value]; ok { return errDuplicateValue } - item := C.calloc(1, C.size_t(unsafe.Sizeof(uintptr(0)))) - t.itemToValue[item] = value - t.valueToItem[value] = item - C.GEOSSTRtree_insert_r(t.context.handle, t.strTree, g.geom, item) + valueHandle := cgo.NewHandle(value) + t.valueHandles[value] = &valueHandle + // FIXME golangci-lint complains about the following line saying: + // dupSubExpr: suspicious identical LHS and RHS for `==` operator (gocritic) + // As the line does not contain an `==` operator, disable gocritic on this + // line. + //nolint:gocritic + C.GEOSSTRtree_insert_r(t.context.cHandle, t.cSTRtree, g.cGeom, unsafe.Pointer(&valueHandle)) return nil } // Iterate calls f for every value in the t. func (t *STRtree) Iterate(callback func(any)) { - handle := cgo.NewHandle(func(item unsafe.Pointer) { - callback(t.itemToValue[item]) + callbackHandle := cgo.NewHandle(func(item unsafe.Pointer) { + valueHandle := (*cgo.Handle)(item) + callback(valueHandle.Value()) }) - defer handle.Delete() - t.context.Lock() - defer t.context.Unlock() + defer callbackHandle.Delete() + t.context.mutex.Lock() + defer t.context.mutex.Unlock() C.GEOSSTRtree_iterate_r( - t.context.handle, - t.strTree, - (*[0]byte)(C.c_GEOSSTRtree_query_callback), // FIXME understand why the cast to *[0]byte is needed - unsafe.Pointer(&handle), //nolint:gocritic + t.context.cHandle, + t.cSTRtree, + (*[0]byte)(C.c_GEOSSTRtree_query_callback), + unsafe.Pointer(&callbackHandle), //nolint:gocritic ) } -// Nearest returns the nearest item in t to value. -func (t *STRtree) Nearest(value any, valueEnvelope *Geom, geomfn func(any) *Geom) any { +// Nearest returns the nearest geometry to geom in t. +// +// WARNING Nearest is currently broken and always panics with a segmentation +// fault. +func (t *STRtree) Nearest(geom *Geom) *Geom { + t.context.mutex.Lock() + defer t.context.mutex.Unlock() + nearestGeom := C.GEOSSTRtree_nearest_r(t.context.cHandle, t.cSTRtree, geom.cGeom) + if nearestGeom == nil { + return nil + } + return t.context.newGeom(nearestGeom, nil) +} + +// NearestGeneric returns the nearest value to value. +// +// WARNING NearestGeneric is currently broken and always panics with a +// segmentation fault. +func (t *STRtree) NearestGeneric(value any, valueEnvelope *Geom, distanceFunc func(any, any) float64) any { if t.context != valueEnvelope.context { panic(errContextMismatch) } - handle := cgo.NewHandle(func(item1, item2 unsafe.Pointer, distance *C.double) C.int { - geom1 := geomfn(t.itemToValue[item1]) - if geom1 == nil { - return 0 - } - geom2 := geomfn(t.itemToValue[item2]) - if geom2 == nil { - return 0 - } - return C.GEOSDistance_r(t.context.handle, geom1.geom, geom2.geom, distance) + callbackHandle := cgo.NewHandle(func(item1, item2 unsafe.Pointer, distance *C.double) C.int { + // FIXME neither item1 not item2 should be nil, but in practice at least + // one of them often is + value1 := (*cgo.Handle)(item1).Value() + value2 := (*cgo.Handle)(item2).Value() + *distance = C.double(distanceFunc(value1, value2)) + return 1 }) - defer handle.Delete() - t.context.Lock() - defer t.context.Unlock() + defer callbackHandle.Delete() + t.context.mutex.Lock() + defer t.context.mutex.Unlock() nearestItem := C.GEOSSTRtree_nearest_generic_r( - t.context.handle, - t.strTree, - t.valueToItem[value], - valueEnvelope.geom, - (*[0]byte)(C.c_GEOSSTRtree_distance_callback), // FIXME understand why the cast to *[0]byte is needed - unsafe.Pointer(&handle), //nolint:gocritic + t.context.cHandle, + t.cSTRtree, + unsafe.Pointer(t.valueHandles[value]), + valueEnvelope.cGeom, + (*[0]byte)(C.c_GEOSSTRtree_distance_callback), + unsafe.Pointer(&callbackHandle), //nolint:gocritic ) - return t.itemToValue[nearestItem] + if nearestItem == nil { + return nil + } + nearestItemHandle := (*cgo.Handle)(nearestItem) + return nearestItemHandle.Value() } // Query calls f with each value that intersects g. func (t *STRtree) Query(g *Geom, callback func(any)) { - handle := cgo.NewHandle(func(elem unsafe.Pointer) { - callback(t.itemToValue[elem]) + callbackHandle := cgo.NewHandle(func(item unsafe.Pointer) { + valueHandle := (*cgo.Handle)(item) + callback(valueHandle.Value()) }) - defer handle.Delete() - t.context.Lock() - defer t.context.Unlock() + defer callbackHandle.Delete() + t.context.mutex.Lock() + defer t.context.mutex.Unlock() C.GEOSSTRtree_query_r( - t.context.handle, - t.strTree, - g.geom, - (*[0]byte)(C.c_GEOSSTRtree_query_callback), // FIXME understand why the cast to *[0]byte is needed - unsafe.Pointer(&handle), //nolint:gocritic + t.context.cHandle, + t.cSTRtree, + g.cGeom, + (*[0]byte)(C.c_GEOSSTRtree_query_callback), + unsafe.Pointer(&callbackHandle), //nolint:gocritic ) } @@ -116,40 +144,44 @@ func (t *STRtree) Remove(g *Geom, value any) bool { if g.context != t.context { panic(errContextMismatch) } - item := t.valueToItem[value] - t.context.Lock() - defer t.context.Unlock() - switch C.GEOSSTRtree_remove_r(t.context.handle, t.strTree, g.geom, item) { + valueHandle := t.valueHandles[value] + t.context.mutex.Lock() + defer t.context.mutex.Unlock() + switch C.GEOSSTRtree_remove_r(t.context.cHandle, t.cSTRtree, g.cGeom, unsafe.Pointer(valueHandle)) { case 0: return false case 1: - delete(t.valueToItem, value) - delete(t.itemToValue, item) - C.free(item) + delete(t.valueHandles, value) + valueHandle.Delete() return true default: panic(t.context.err) } } -func (t *STRtree) finalize() { - if t.context == nil { - return - } - if t.context.strTreeFinalizeFunc != nil { - t.context.strTreeFinalizeFunc(t) +func (c *Context) destroySTRtree(cSTRtree *C.struct_GEOSSTRtree_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSSTRtree_destroy_r(c.cHandle, cSTRtree) + c.unref() +} + +func destoryValueHandles(valueHandles map[any]*cgo.Handle) { + for _, valueHandle := range valueHandles { + valueHandle.Delete() } - t.Destroy() } //export go_GEOSSTRtree_distance_callback func go_GEOSSTRtree_distance_callback(item1, item2 unsafe.Pointer, distance *C.double, userdata unsafe.Pointer) C.int { - handle := *(*cgo.Handle)(userdata) - return handle.Value().(func(unsafe.Pointer, unsafe.Pointer, *C.double) C.int)(item1, item2, distance) //nolint:forcetypeassert + distanceCallbackHandle := (*cgo.Handle)(userdata) + distanceCallback := distanceCallbackHandle.Value().(func(unsafe.Pointer, unsafe.Pointer, *C.double) C.int) //nolint:forcetypeassert,revive + return distanceCallback(item1, item2, distance) } //export go_GEOSSTRtree_query_callback -func go_GEOSSTRtree_query_callback(elem, userdata unsafe.Pointer) { - handle := *(*cgo.Handle)(userdata) - handle.Value().(func(unsafe.Pointer))(elem) //nolint:forcetypeassert +func go_GEOSSTRtree_query_callback(item, userdata unsafe.Pointer) { + callbackHandle := (*cgo.Handle)(userdata) + callback := callbackHandle.Value().(func(unsafe.Pointer)) //nolint:forcetypeassert,revive + callback(item) } diff --git a/vendor/github.com/twpayne/go-geos/wkbreader.go b/vendor/github.com/twpayne/go-geos/wkbreader.go new file mode 100644 index 00000000..b5af620a --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/wkbreader.go @@ -0,0 +1,46 @@ +package geos + +// #include +// #include "go-geos.h" +import "C" + +import ( + "runtime" +) + +// A WKBReader reads geometries from WKB (Well Known Binary). +type WKBReader struct { + context *Context + cWKBReader *C.struct_GEOSWKBReader_t +} + +// NewWKBReader returns a new WKBReader. +func (c *Context) NewWKBReader() *WKBReader { + c.mutex.Lock() + defer c.mutex.Unlock() + cWKBReader := C.GEOSWKBReader_create_r(c.cHandle) + wkbReader := &WKBReader{ + context: c, + cWKBReader: cWKBReader, + } + c.ref() + runtime.AddCleanup(wkbReader, c.destroyWKBReader, cWKBReader) + return wkbReader +} + +// Read reads a geometry from wkb. +func (r *WKBReader) Read(wkb []byte) (*Geom, error) { + r.context.mutex.Lock() + defer r.context.mutex.Unlock() + wkbCBuf := C.CBytes(wkb) + defer C.free(wkbCBuf) + r.context.err = nil + return r.context.newGeom(C.GEOSWKBReader_read_r(r.context.cHandle, r.cWKBReader, (*C.uchar)(wkbCBuf), C.ulong(len(wkb))), nil), r.context.err +} + +func (c *Context) destroyWKBReader(cWKBReader *C.struct_GEOSWKBReader_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSWKBReader_destroy_r(c.cHandle, cWKBReader) + c.unref() +} diff --git a/vendor/github.com/twpayne/go-geos/wkbwriter.go b/vendor/github.com/twpayne/go-geos/wkbwriter.go new file mode 100644 index 00000000..1b1115af --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/wkbwriter.go @@ -0,0 +1,75 @@ +package geos + +// #include "go-geos.h" +import "C" + +import ( + "runtime" + "unsafe" +) + +// A WKBFlavor is a flavor of WKB. +type WKBFlavor int + +// WKB flavors. +const ( + WKBFlavorExtended WKBFlavor = C.GEOS_WKB_EXTENDED + WKBFlavorISO WKBFlavor = C.GEOS_WKB_ISO +) + +// A WKBWriter writes geometries as WKB (Well Known Binary). +type WKBWriter struct { + context *Context + cWKBWriter *C.struct_GEOSWKBWriter_t +} + +// A WKBWriterOption sets an option on a WKBWriter. +type WKBWriterOption func(*WKBWriter) + +// WithWKBWriterFlavor sets the WKB flavor. +func WithWKBWriterFlavor(flavor WKBFlavor) WKBWriterOption { + return func(w *WKBWriter) { + C.GEOSWKBWriter_setFlavor_r(w.context.cHandle, w.cWKBWriter, C.int(flavor)) + } +} + +// WithWKBWriterIncludeSRID sets whether to include the SRID. +func WithWKBWriterIncludeSRID(includeSRID bool) WKBWriterOption { + return func(w *WKBWriter) { + C.GEOSWKBWriter_setIncludeSRID_r(w.context.cHandle, w.cWKBWriter, toInt[C.char](includeSRID)) + } +} + +// NewWKBWriter returns a new WKBWriter with the given options. +func (c *Context) NewWKBWriter(options ...WKBWriterOption) *WKBWriter { + c.mutex.Lock() + defer c.mutex.Unlock() + cWKBWriter := C.GEOSWKBWriter_create_r(c.cHandle) + wkbWriter := &WKBWriter{ + context: c, + cWKBWriter: cWKBWriter, + } + c.ref() + runtime.AddCleanup(wkbWriter, c.destroyWKBWriter, cWKBWriter) + for _, option := range options { + option(wkbWriter) + } + return wkbWriter +} + +// Write returns the WKB representation of g. +func (w *WKBWriter) Write(g *Geom) []byte { + w.context.mutex.Lock() + defer w.context.mutex.Unlock() + var size C.size_t + cWKBBuf := C.GEOSWKBWriter_write_r(g.context.cHandle, w.cWKBWriter, g.cGeom, &size) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer(cWKBBuf)) + return C.GoBytes(unsafe.Pointer(cWKBBuf), C.int(size)) +} + +func (c *Context) destroyWKBWriter(cWKBWriter *C.struct_GEOSWKBWriter_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSWKBWriter_destroy_r(c.cHandle, cWKBWriter) + c.unref() +} diff --git a/vendor/github.com/twpayne/go-geos/wktreader.go b/vendor/github.com/twpayne/go-geos/wktreader.go new file mode 100644 index 00000000..a3939ba5 --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/wktreader.go @@ -0,0 +1,47 @@ +package geos + +// #include +// #include "go-geos.h" +import "C" + +import ( + "runtime" + "unsafe" +) + +// A WKTReader reads geometries from WKT (Well Known Text). +type WKTReader struct { + context *Context + cWKTReader *C.struct_GEOSWKTReader_t +} + +// NewWKTReader returns a new WKTReader. +func (c *Context) NewWKTReader() *WKTReader { + c.mutex.Lock() + defer c.mutex.Unlock() + cWKTReader := C.GEOSWKTReader_create_r(c.cHandle) + wktReader := &WKTReader{ + context: c, + cWKTReader: cWKTReader, + } + c.ref() + runtime.AddCleanup(wktReader, c.destroyWKTReader, cWKTReader) + return wktReader +} + +// Read reads a geometry from wkt. +func (r *WKTReader) Read(wkt string) (*Geom, error) { + r.context.mutex.Lock() + defer r.context.mutex.Unlock() + wktCStr := C.CString(wkt) + defer C.free(unsafe.Pointer(wktCStr)) + r.context.err = nil + return r.context.newGeom(C.GEOSWKTReader_read_r(r.context.cHandle, r.cWKTReader, wktCStr), nil), r.context.err +} + +func (c *Context) destroyWKTReader(cWKTReader *C.struct_GEOSWKTReader_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSWKTReader_destroy_r(c.cHandle, cWKTReader) + c.unref() +} diff --git a/vendor/github.com/twpayne/go-geos/wktwriter.go b/vendor/github.com/twpayne/go-geos/wktwriter.go new file mode 100644 index 00000000..c1e75d27 --- /dev/null +++ b/vendor/github.com/twpayne/go-geos/wktwriter.go @@ -0,0 +1,45 @@ +package geos + +// #include "go-geos.h" +import "C" + +import ( + "runtime" + "unsafe" +) + +// A WKTWriter writes geometries in WKT (Well Known Text). +type WKTWriter struct { + context *Context + cWKTWriter *C.struct_GEOSWKTWriter_t +} + +// NewWKTWriter returns a new WKTWriter. +func (c *Context) NewWKTWriter() *WKTWriter { + c.mutex.Lock() + defer c.mutex.Unlock() + cWKTWriter := C.GEOSWKTWriter_create_r(c.cHandle) + wktWriter := &WKTWriter{ + context: c, + cWKTWriter: cWKTWriter, + } + c.ref() + runtime.AddCleanup(wktWriter, c.destroyWKTWriter, cWKTWriter) + return wktWriter +} + +// Write returns the WKT representation of g. +func (w *WKTWriter) Write(g *Geom) string { + w.context.mutex.Lock() + defer w.context.mutex.Unlock() + cWKTStr := C.GEOSWKTWriter_write_r(w.context.cHandle, w.cWKTWriter, g.cGeom) + defer C.GEOSFree_r(g.context.cHandle, unsafe.Pointer(cWKTStr)) + return C.GoString(cWKTStr) +} + +func (c *Context) destroyWKTWriter(cWKTWriter *C.struct_GEOSWKTWriter_t) { + c.mutex.Lock() + defer c.mutex.Unlock() + C.GEOSWKTWriter_destroy_r(c.cHandle, cWKTWriter) + c.unref() +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 67d9501d..ec9a1aa9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -112,8 +112,8 @@ github.com/savsgio/gotils/strconv # github.com/tinylib/msgp v1.1.8 ## explicit; go 1.15 github.com/tinylib/msgp/msgp -# github.com/twpayne/go-geos v0.13.2 -## explicit; go 1.18 +# github.com/twpayne/go-geos v0.20.2 +## explicit; go 1.24.0 github.com/twpayne/go-geos # github.com/valyala/bytebufferpool v1.0.0 ## explicit