refactored geos package
parent
5eed0502ea
commit
2a699a09c8
|
@ -4,10 +4,13 @@ import (
|
|||
"errors"
|
||||
"goposm/geom/geos"
|
||||
"goposm/geom/ogr"
|
||||
"goposm/logging"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var log = logging.NewLogger("Clipper")
|
||||
|
||||
// Tile bbox into multiple sub-boxes, each of `width` size.
|
||||
|
||||
// >>> list(tile_bbox((-1, 1, 0.49, 1.51), 0.5)) #doctest: +NORMALIZE_WHITESPACE
|
||||
|
@ -140,13 +143,16 @@ func filterGeometryByType(g *geos.Geos, geom *geos.Geom, targetType string) []*g
|
|||
for _, part := range g.Geoms(geom) {
|
||||
// only parts with same type
|
||||
if g.Type(part) == targetType {
|
||||
geoms = append(geoms, part)
|
||||
geoms = append(geoms, g.Clone(part))
|
||||
}
|
||||
}
|
||||
g.Destroy(geom)
|
||||
if len(geoms) != 0 {
|
||||
return geoms
|
||||
}
|
||||
return []*geos.Geom{}
|
||||
}
|
||||
g.Destroy(geom)
|
||||
return []*geos.Geom{}
|
||||
}
|
||||
|
||||
|
@ -164,21 +170,21 @@ func (clipper *Clipper) Clip(geom *geos.Geom) ([]*geos.Geom, error) {
|
|||
var intersections []*geos.Geom
|
||||
|
||||
for _, hit := range hits {
|
||||
hit.Lock.Lock()
|
||||
hit.Lock()
|
||||
if g.PreparedContains(hit.Prepared, geom) {
|
||||
hit.Lock.Unlock()
|
||||
hit.Unlock()
|
||||
return []*geos.Geom{geom}, nil
|
||||
}
|
||||
|
||||
if g.PreparedIntersects(hit.Prepared, geom) {
|
||||
hit.Lock.Unlock()
|
||||
hit.Unlock()
|
||||
newPart := g.Intersection(hit.Geom, geom)
|
||||
newParts := filterGeometryByType(g, newPart, geomType)
|
||||
for _, p := range newParts {
|
||||
intersections = append(intersections, p)
|
||||
}
|
||||
} else {
|
||||
hit.Lock.Unlock()
|
||||
hit.Unlock()
|
||||
}
|
||||
}
|
||||
return mergeGeometries(g, intersections, geomType), nil
|
||||
|
@ -188,9 +194,15 @@ func flattenPolygons(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom {
|
|||
var result []*geos.Geom
|
||||
for _, geom := range geoms {
|
||||
if g.Type(geom) == "MultiPolygon" {
|
||||
result = append(result, g.Geoms(geom)...)
|
||||
} else {
|
||||
for _, part := range g.Geoms(geom) {
|
||||
result = append(result, g.Clone(part))
|
||||
}
|
||||
g.Destroy(geom)
|
||||
} else if g.Type(geom) == "Polygon" {
|
||||
result = append(result, geom)
|
||||
} else {
|
||||
log.Printf("unexpected geometry type in flattenPolygons")
|
||||
g.Destroy(geom)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -200,9 +212,15 @@ func flattenLineStrings(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom {
|
|||
var result []*geos.Geom
|
||||
for _, geom := range geoms {
|
||||
if g.Type(geom) == "MultiLineString" {
|
||||
result = append(result, g.Geoms(geom)...)
|
||||
} else {
|
||||
for _, part := range g.Geoms(geom) {
|
||||
result = append(result, g.Clone(part))
|
||||
}
|
||||
g.Destroy(geom)
|
||||
} else if g.Type(geom) == "LineString" {
|
||||
result = append(result, geom)
|
||||
} else {
|
||||
log.Printf("unexpected geometry type in flattenPolygons")
|
||||
g.Destroy(geom)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -213,6 +231,8 @@ func filterInvalidLineStrings(g *geos.Geos, geoms []*geos.Geom) []*geos.Geom {
|
|||
for _, geom := range geoms {
|
||||
if geom.Length() > 1e-9 {
|
||||
result = append(result, geom)
|
||||
} else {
|
||||
g.Destroy(geom)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
|
16
geom/geom.go
16
geom/geom.go
|
@ -65,12 +65,15 @@ func LineStringWkb(g *geos.Geos, nodes []element.Node) (*element.Geometry, error
|
|||
coordSeq.SetXY(g, uint32(i), nd.Long, nd.Lat)
|
||||
}
|
||||
geom, err := coordSeq.AsLineString(g)
|
||||
wkb := g.AsWkb(geom)
|
||||
if wkb == nil {
|
||||
g.Destroy(geom)
|
||||
return nil, errors.New("could not create wkb")
|
||||
if err != nil {
|
||||
// coordSeq gets Destroy by GEOS
|
||||
return nil, err
|
||||
}
|
||||
g.DestroyLater(geom)
|
||||
wkb := g.AsWkb(geom)
|
||||
if wkb == nil {
|
||||
return nil, errors.New("could not create wkb")
|
||||
}
|
||||
return &element.Geometry{
|
||||
Wkb: wkb,
|
||||
Geom: geom,
|
||||
|
@ -97,7 +100,8 @@ func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// coordSeq inherited by LineString, no destroy
|
||||
|
||||
// coordSeq inherited by LinearRing, no destroy
|
||||
for i, nd := range nodes {
|
||||
err := coordSeq.SetXY(g, uint32(i), nd.Long, nd.Lat)
|
||||
if err != nil {
|
||||
|
@ -106,7 +110,7 @@ func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
|
|||
}
|
||||
ring, err := coordSeq.AsLinearRing(g)
|
||||
if err != nil {
|
||||
g.DestroyCoordSeq(coordSeq)
|
||||
// coordSeq gets Destroy by GEOS
|
||||
return nil, err
|
||||
}
|
||||
// ring inherited by Polygon, no destroy
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package geos
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lgeos_c
|
||||
#include "geos_c.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type CoordSeq struct {
|
||||
v *C.GEOSCoordSequence
|
||||
}
|
||||
|
||||
func (this *Geos) CreateCoordSeq(size, dim uint32) (*CoordSeq, error) {
|
||||
result := C.GEOSCoordSeq_create_r(this.v, C.uint(size), C.uint(dim))
|
||||
if result == nil {
|
||||
return nil, CreateError("could not create CoordSeq")
|
||||
}
|
||||
return &CoordSeq{result}, nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) SetXY(handle *Geos, i uint32, x, y float64) error {
|
||||
if C.GEOSCoordSeq_setX_r(handle.v, this.v, C.uint(i), C.double(x)) == 0 {
|
||||
return Error("unable to SetY")
|
||||
}
|
||||
if C.GEOSCoordSeq_setY_r(handle.v, this.v, C.uint(i), C.double(y)) == 0 {
|
||||
return Error("unable to SetX")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) AsPoint(handle *Geos) (*Geom, error) {
|
||||
geom := C.GEOSGeom_createPoint_r(handle.v, this.v)
|
||||
if geom == nil {
|
||||
return nil, CreateError("unable to create Point")
|
||||
}
|
||||
return &Geom{geom}, nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) AsLineString(handle *Geos) (*Geom, error) {
|
||||
geom := C.GEOSGeom_createLineString_r(handle.v, this.v)
|
||||
if geom == nil {
|
||||
return nil, CreateError("unable to create LineString")
|
||||
}
|
||||
return &Geom{geom}, nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) AsLinearRing(handle *Geos) (*Geom, error) {
|
||||
ring := C.GEOSGeom_createLinearRing_r(handle.v, this.v)
|
||||
if ring == nil {
|
||||
return nil, CreateError("unable to create LinearRing")
|
||||
}
|
||||
return &Geom{ring}, nil
|
||||
}
|
||||
|
||||
func (this *Geos) DestroyCoordSeq(coordSeq *CoordSeq) {
|
||||
if coordSeq.v != nil {
|
||||
C.GEOSCoordSeq_destroy_r(this.v, coordSeq.v)
|
||||
coordSeq.v = nil
|
||||
} else {
|
||||
panic("double free?")
|
||||
}
|
||||
}
|
|
@ -9,18 +9,12 @@ extern void goLogString(char *msg);
|
|||
extern void debug_wrap(const char *fmt, ...);
|
||||
extern GEOSContextHandle_t initGEOS_r_debug();
|
||||
extern void initGEOS_debug();
|
||||
extern void IndexQuerySendCallback(void *, void *);
|
||||
extern void goIndexSendQueryResult(size_t, void *);
|
||||
extern void IndexQuery(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, void *);
|
||||
extern void IndexAdd(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, size_t);
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"goposm/logging"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -39,10 +33,6 @@ type Geom struct {
|
|||
v *C.GEOSGeometry
|
||||
}
|
||||
|
||||
type PreparedGeom struct {
|
||||
v *C.GEOSPreparedGeometry
|
||||
}
|
||||
|
||||
type CreateError string
|
||||
type Error string
|
||||
|
||||
|
@ -79,50 +69,34 @@ func init() {
|
|||
C.initGEOS_debug()
|
||||
}
|
||||
|
||||
type CoordSeq struct {
|
||||
v *C.GEOSCoordSequence
|
||||
func (this *Geos) Destroy(geom *Geom) {
|
||||
runtime.SetFinalizer(geom, nil)
|
||||
if geom.v != nil {
|
||||
C.GEOSGeom_destroy_r(this.v, geom.v)
|
||||
geom.v = nil
|
||||
} else {
|
||||
log.Printf("double free?")
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Geos) CreateCoordSeq(size, dim uint32) (*CoordSeq, error) {
|
||||
result := C.GEOSCoordSeq_create_r(this.v, C.uint(size), C.uint(dim))
|
||||
func destroyGeom(geom *Geom) {
|
||||
C.GEOSGeom_destroy(geom.v)
|
||||
}
|
||||
|
||||
func (this *Geos) DestroyLater(geom *Geom) {
|
||||
runtime.SetFinalizer(geom, destroyGeom)
|
||||
}
|
||||
|
||||
func (this *Geos) Clone(geom *Geom) *Geom {
|
||||
if geom == nil || geom.v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := C.GEOSGeom_clone_r(this.v, geom.v)
|
||||
if result == nil {
|
||||
return nil, CreateError("could not create CoordSeq")
|
||||
return nil
|
||||
}
|
||||
return &CoordSeq{result}, nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) SetXY(handle *Geos, i uint32, x, y float64) error {
|
||||
if C.GEOSCoordSeq_setX_r(handle.v, this.v, C.uint(i), C.double(x)) == 0 {
|
||||
return Error("unable to SetY")
|
||||
}
|
||||
if C.GEOSCoordSeq_setY_r(handle.v, this.v, C.uint(i), C.double(y)) == 0 {
|
||||
return Error("unable to SetX")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) AsPoint(handle *Geos) (*Geom, error) {
|
||||
geom := C.GEOSGeom_createPoint_r(handle.v, this.v)
|
||||
if geom == nil {
|
||||
return nil, CreateError("unable to create Point")
|
||||
}
|
||||
return &Geom{geom}, nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) AsLineString(handle *Geos) (*Geom, error) {
|
||||
geom := C.GEOSGeom_createLineString_r(handle.v, this.v)
|
||||
if geom == nil {
|
||||
return nil, CreateError("unable to create LineString")
|
||||
}
|
||||
return &Geom{geom}, nil
|
||||
}
|
||||
|
||||
func (this *CoordSeq) AsLinearRing(handle *Geos) (*Geom, error) {
|
||||
ring := C.GEOSGeom_createLinearRing_r(handle.v, this.v)
|
||||
if ring == nil {
|
||||
return nil, CreateError("unable to create LinearRing")
|
||||
}
|
||||
return &Geom{ring}, nil
|
||||
return &Geom{result}
|
||||
}
|
||||
|
||||
func (this *Geos) CreatePolygon(shell *Geom, holes []*Geom) *Geom {
|
||||
|
@ -136,16 +110,6 @@ func (this *Geos) CreatePolygon(shell *Geom, holes []*Geom) *Geom {
|
|||
return &Geom{polygon}
|
||||
}
|
||||
|
||||
func (this *Geos) FromWkt(wkt string) (geom *Geom) {
|
||||
wktC := C.CString(wkt)
|
||||
defer C.free(unsafe.Pointer(wktC))
|
||||
return &Geom{C.GEOSGeomFromWKT_r(this.v, wktC)}
|
||||
}
|
||||
|
||||
func (this *Geos) Buffer(geom *Geom, size float64) *Geom {
|
||||
return &Geom{C.GEOSBuffer_r(this.v, geom.v, C.double(size), 50)}
|
||||
}
|
||||
|
||||
func (this *Geos) NumGeoms(geom *Geom) int32 {
|
||||
count := int32(C.GEOSGetNumGeometries_r(this.v, geom.v))
|
||||
return count
|
||||
|
@ -164,89 +128,6 @@ func (this *Geos) Geoms(geom *Geom) []*Geom {
|
|||
return result
|
||||
}
|
||||
|
||||
func (this *Geos) Contains(a, b *Geom) bool {
|
||||
result := C.GEOSContains_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geos) Intersects(a, b *Geom) bool {
|
||||
result := C.GEOSIntersects_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geos) Prepare(geom *Geom) *PreparedGeom {
|
||||
prep := C.GEOSPrepare_r(this.v, geom.v)
|
||||
if prep == nil {
|
||||
return nil
|
||||
}
|
||||
return &PreparedGeom{prep}
|
||||
}
|
||||
|
||||
func (this *Geos) PreparedContains(a *PreparedGeom, b *Geom) bool {
|
||||
result := C.GEOSPreparedContains_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geos) PreparedIntersects(a *PreparedGeom, b *Geom) bool {
|
||||
// fmt.Println(this.Type(b))
|
||||
result := C.GEOSPreparedIntersects_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geos) Intersection(a, b *Geom) *Geom {
|
||||
result := C.GEOSIntersection_r(this.v, a.v, b.v)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
geom := &Geom{result}
|
||||
this.DestroyLater(geom)
|
||||
return geom
|
||||
}
|
||||
|
||||
func (this *Geos) UnionPolygons(polygons []*Geom) *Geom {
|
||||
multiPolygon := this.MultiPolygon(polygons)
|
||||
if multiPolygon == nil {
|
||||
return nil
|
||||
}
|
||||
result := C.GEOSUnaryUnion_r(this.v, multiPolygon.v)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
return &Geom{result}
|
||||
}
|
||||
|
||||
func (this *Geos) LineMerge(lines []*Geom) []*Geom {
|
||||
multiLineString := this.MultiLineString(lines)
|
||||
if multiLineString == nil {
|
||||
return nil
|
||||
}
|
||||
result := C.GEOSLineMerge_r(this.v, multiLineString.v)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
geom := &Geom{result}
|
||||
if this.Type(geom) == "LineString" {
|
||||
return []*Geom{geom}
|
||||
}
|
||||
return this.Geoms(geom)
|
||||
}
|
||||
|
||||
func (this *Geos) ExteriorRing(geom *Geom) *Geom {
|
||||
ring := C.GEOSGetExteriorRing_r(this.v, geom.v)
|
||||
if ring == nil {
|
||||
|
@ -349,43 +230,6 @@ func (this *Geos) MultiLineString(lines []*Geom) *Geom {
|
|||
return &Geom{geom}
|
||||
}
|
||||
|
||||
func (this *Geos) AsWkt(geom *Geom) string {
|
||||
str := C.GEOSGeomToWKT_r(this.v, geom.v)
|
||||
result := C.GoString(str)
|
||||
C.free(unsafe.Pointer(str))
|
||||
return result
|
||||
}
|
||||
func (this *Geos) AsWkb(geom *Geom) []byte {
|
||||
var size C.size_t
|
||||
buf := C.GEOSGeomToWKB_buf_r(this.v, geom.v, &size)
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
result := C.GoBytes(unsafe.Pointer(buf), C.int(size))
|
||||
C.free(unsafe.Pointer(buf))
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *Geos) FromWkb(wkb []byte) *Geom {
|
||||
geom := C.GEOSGeomFromWKB_buf_r(this.v, (*C.uchar)(&wkb[0]), C.size_t(len(wkb)))
|
||||
if geom == nil {
|
||||
return nil
|
||||
}
|
||||
return &Geom{geom}
|
||||
}
|
||||
|
||||
func (this *Geos) Clone(geom *Geom) *Geom {
|
||||
if geom == nil || geom.v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := C.GEOSGeom_clone_r(this.v, geom.v)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
return &Geom{result}
|
||||
}
|
||||
|
||||
func (this *Geos) IsValid(geom *Geom) bool {
|
||||
if C.GEOSisValid_r(this.v, geom.v) == 1 {
|
||||
return true
|
||||
|
@ -409,6 +253,14 @@ func (this *Geos) Type(geom *Geom) string {
|
|||
return C.GoString(geomType)
|
||||
}
|
||||
|
||||
func (this *Geos) Equals(a, b *Geom) bool {
|
||||
result := C.GEOSEquals_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geom) Area() float64 {
|
||||
var area C.double
|
||||
if ret := C.GEOSArea(this.v, &area); ret == 1 {
|
||||
|
@ -427,12 +279,11 @@ func (this *Geom) Length() float64 {
|
|||
}
|
||||
}
|
||||
|
||||
func (this *Geos) Equals(a, b *Geom) bool {
|
||||
result := C.GEOSEquals_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
type Bounds struct {
|
||||
MinX float64
|
||||
MinY float64
|
||||
MaxX float64
|
||||
MaxY float64
|
||||
}
|
||||
|
||||
var NilBounds = Bounds{1e20, 1e20, -1e20, -1e20}
|
||||
|
@ -475,86 +326,3 @@ func (this *Geom) Bounds() Bounds {
|
|||
|
||||
return Bounds{minx, miny, maxx, maxy}
|
||||
}
|
||||
|
||||
type Bounds struct {
|
||||
MinX float64
|
||||
MinY float64
|
||||
MaxX float64
|
||||
MaxY float64
|
||||
}
|
||||
|
||||
func (this *Geos) Destroy(geom *Geom) {
|
||||
if geom.v != nil {
|
||||
C.GEOSGeom_destroy_r(this.v, geom.v)
|
||||
geom.v = nil
|
||||
} else {
|
||||
panic("double free?")
|
||||
}
|
||||
}
|
||||
|
||||
func destroyGeom(geom *Geom) {
|
||||
C.GEOSGeom_destroy(geom.v)
|
||||
}
|
||||
|
||||
func (this *Geos) DestroyLater(geom *Geom) {
|
||||
runtime.SetFinalizer(geom, destroyGeom)
|
||||
}
|
||||
|
||||
func (this *Geos) DestroyCoordSeq(coordSeq *CoordSeq) {
|
||||
if coordSeq.v != nil {
|
||||
C.GEOSCoordSeq_destroy_r(this.v, coordSeq.v)
|
||||
coordSeq.v = nil
|
||||
} else {
|
||||
panic("double free?")
|
||||
}
|
||||
}
|
||||
|
||||
type indexGeom struct {
|
||||
Geom *Geom
|
||||
Lock *sync.Mutex
|
||||
Prepared *PreparedGeom
|
||||
}
|
||||
type Index struct {
|
||||
v *C.GEOSSTRtree
|
||||
geoms []indexGeom
|
||||
}
|
||||
|
||||
func (this *Geos) CreateIndex() *Index {
|
||||
tree := C.GEOSSTRtree_create_r(this.v, 10)
|
||||
if tree == nil {
|
||||
panic("unable to create tree")
|
||||
}
|
||||
return &Index{tree, []indexGeom{}}
|
||||
}
|
||||
|
||||
// IndexQuery adds a geom to the index with the id.
|
||||
func (this *Geos) IndexAdd(index *Index, geom *Geom) {
|
||||
id := len(index.geoms)
|
||||
C.IndexAdd(this.v, index.v, geom.v, C.size_t(id))
|
||||
prep := this.Prepare(geom)
|
||||
index.geoms = append(index.geoms, indexGeom{geom, &sync.Mutex{}, prep})
|
||||
}
|
||||
|
||||
// IndexQuery queries the index for intersections with geom.
|
||||
func (this *Geos) IndexQuery(index *Index, geom *Geom) []indexGeom {
|
||||
hits := make(chan int)
|
||||
go func() {
|
||||
//
|
||||
// using a pointer to our hits chan to pass it through
|
||||
// C.IndexQuerySendCallback (in C.IndexQuery) back
|
||||
// to goIndexSendQueryResult
|
||||
C.IndexQuery(this.v, index.v, geom.v, unsafe.Pointer(&hits))
|
||||
close(hits)
|
||||
}()
|
||||
var geoms []indexGeom
|
||||
for idx := range hits {
|
||||
geoms = append(geoms, index.geoms[idx])
|
||||
}
|
||||
return geoms
|
||||
}
|
||||
|
||||
//export goIndexSendQueryResult
|
||||
func goIndexSendQueryResult(id C.size_t, ptr unsafe.Pointer) {
|
||||
results := *(*chan int)(ptr)
|
||||
results <- int(id)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package geos
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lgeos_c
|
||||
#include "geos_c.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
extern void IndexQuerySendCallback(void *, void *);
|
||||
extern void goIndexSendQueryResult(size_t, void *);
|
||||
extern void IndexQuery(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, void *);
|
||||
extern void IndexAdd(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, size_t);
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IndexGeom is a struct for indexed geometries used by Index
|
||||
// and returned by IndexQuery. Access to Prepared requires acquiring .Lock()
|
||||
type IndexGeom struct {
|
||||
*sync.Mutex // Mutex for Prepared
|
||||
Geom *Geom
|
||||
Prepared *PreparedGeom
|
||||
}
|
||||
type Index struct {
|
||||
v *C.GEOSSTRtree
|
||||
geoms []IndexGeom
|
||||
}
|
||||
|
||||
func (this *Geos) CreateIndex() *Index {
|
||||
tree := C.GEOSSTRtree_create_r(this.v, 10)
|
||||
if tree == nil {
|
||||
panic("unable to create tree")
|
||||
}
|
||||
return &Index{tree, []IndexGeom{}}
|
||||
}
|
||||
|
||||
// IndexQuery adds a geom to the index with the id.
|
||||
func (this *Geos) IndexAdd(index *Index, geom *Geom) {
|
||||
id := len(index.geoms)
|
||||
C.IndexAdd(this.v, index.v, geom.v, C.size_t(id))
|
||||
prep := this.Prepare(geom)
|
||||
index.geoms = append(index.geoms, IndexGeom{&sync.Mutex{}, geom, prep})
|
||||
}
|
||||
|
||||
// IndexQuery queries the index for intersections with geom.
|
||||
func (this *Geos) IndexQuery(index *Index, geom *Geom) []IndexGeom {
|
||||
hits := make(chan int)
|
||||
go func() {
|
||||
// using a pointer to our hits chan to pass it through
|
||||
// C.IndexQuerySendCallback (in C.IndexQuery) back
|
||||
// to goIndexSendQueryResult
|
||||
C.IndexQuery(this.v, index.v, geom.v, unsafe.Pointer(&hits))
|
||||
close(hits)
|
||||
}()
|
||||
var geoms []IndexGeom
|
||||
for idx := range hits {
|
||||
geoms = append(geoms, index.geoms[idx])
|
||||
}
|
||||
return geoms
|
||||
}
|
||||
|
||||
//export goIndexSendQueryResult
|
||||
func goIndexSendQueryResult(id C.size_t, ptr unsafe.Pointer) {
|
||||
results := *(*chan int)(ptr)
|
||||
results <- int(id)
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package geos
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lgeos_c
|
||||
#include "geos_c.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (this *Geos) Contains(a, b *Geom) bool {
|
||||
result := C.GEOSContains_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geos) Intersects(a, b *Geom) bool {
|
||||
result := C.GEOSIntersects_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geos) Intersection(a, b *Geom) *Geom {
|
||||
result := C.GEOSIntersection_r(this.v, a.v, b.v)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
geom := &Geom{result}
|
||||
return geom
|
||||
}
|
||||
|
||||
func (this *Geos) Buffer(geom *Geom, size float64) *Geom {
|
||||
buffered := C.GEOSBuffer_r(this.v, geom.v, C.double(size), 50)
|
||||
if buffered == nil {
|
||||
return nil
|
||||
}
|
||||
return &Geom{buffered}
|
||||
}
|
||||
|
||||
// UnionPolygons tries to merge polygons.
|
||||
// Returns a single (Multi)Polygon.
|
||||
// Destroys polygons and returns new allocated (Multi)Polygon as necessary.
|
||||
func (this *Geos) UnionPolygons(polygons []*Geom) *Geom {
|
||||
if len(polygons) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(polygons) == 1 {
|
||||
return polygons[0]
|
||||
}
|
||||
multiPolygon := this.MultiPolygon(polygons)
|
||||
if multiPolygon == nil {
|
||||
return nil
|
||||
}
|
||||
defer this.Destroy(multiPolygon)
|
||||
|
||||
result := C.GEOSUnaryUnion_r(this.v, multiPolygon.v)
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
return &Geom{result}
|
||||
}
|
||||
|
||||
// LineMerge tries to merge lines. Returns slice of LineStrings.
|
||||
// Destroys lines and returns new allocated LineString Geoms.
|
||||
func (this *Geos) LineMerge(lines []*Geom) []*Geom {
|
||||
if len(lines) <= 1 {
|
||||
return lines
|
||||
}
|
||||
multiLineString := this.MultiLineString(lines)
|
||||
if multiLineString == nil {
|
||||
return nil
|
||||
}
|
||||
defer this.Destroy(multiLineString)
|
||||
merged := C.GEOSLineMerge_r(this.v, multiLineString.v)
|
||||
if merged == nil {
|
||||
return nil
|
||||
}
|
||||
geom := &Geom{merged}
|
||||
if this.Type(geom) == "LineString" {
|
||||
return []*Geom{geom}
|
||||
}
|
||||
|
||||
// extract MultiLineString
|
||||
lines = make([]*Geom, 0, this.NumGeoms(geom))
|
||||
for _, line := range this.Geoms(geom) {
|
||||
lines = append(lines, this.Clone(line))
|
||||
}
|
||||
this.Destroy(geom)
|
||||
return lines
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package geos
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lgeos_c
|
||||
#include "geos_c.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type PreparedGeom struct {
|
||||
v *C.GEOSPreparedGeometry
|
||||
}
|
||||
|
||||
func (this *Geos) Prepare(geom *Geom) *PreparedGeom {
|
||||
prep := C.GEOSPrepare_r(this.v, geom.v)
|
||||
if prep == nil {
|
||||
return nil
|
||||
}
|
||||
return &PreparedGeom{prep}
|
||||
}
|
||||
|
||||
func (this *Geos) PreparedContains(a *PreparedGeom, b *Geom) bool {
|
||||
result := C.GEOSPreparedContains_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Geos) PreparedIntersects(a *PreparedGeom, b *Geom) bool {
|
||||
result := C.GEOSPreparedIntersects_r(this.v, a.v, b.v)
|
||||
if result == 1 {
|
||||
return true
|
||||
}
|
||||
// result == 2 -> exception (already logged to console)
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package geos
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lgeos_c
|
||||
#include "geos_c.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (this *Geos) FromWkt(wkt string) *Geom {
|
||||
wktC := C.CString(wkt)
|
||||
defer C.free(unsafe.Pointer(wktC))
|
||||
geom := C.GEOSGeomFromWKT_r(this.v, wktC)
|
||||
if geom == nil {
|
||||
return nil
|
||||
}
|
||||
return &Geom{geom}
|
||||
}
|
||||
|
||||
func (this *Geos) FromWkb(wkb []byte) *Geom {
|
||||
if len(wkb) == 0 {
|
||||
return nil
|
||||
}
|
||||
geom := C.GEOSGeomFromWKB_buf_r(this.v, (*C.uchar)(&wkb[0]), C.size_t(len(wkb)))
|
||||
if geom == nil {
|
||||
return nil
|
||||
}
|
||||
return &Geom{geom}
|
||||
}
|
||||
|
||||
func (this *Geos) AsWkt(geom *Geom) string {
|
||||
str := C.GEOSGeomToWKT_r(this.v, geom.v)
|
||||
if str == nil {
|
||||
return ""
|
||||
}
|
||||
result := C.GoString(str)
|
||||
C.free(unsafe.Pointer(str))
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *Geos) AsWkb(geom *Geom) []byte {
|
||||
var size C.size_t
|
||||
buf := C.GEOSGeomToWKB_buf_r(this.v, geom.v, &size)
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
result := C.GoBytes(unsafe.Pointer(buf), C.int(size))
|
||||
C.free(unsafe.Pointer(buf))
|
||||
return result
|
||||
}
|
Loading…
Reference in New Issue