2013-05-06 21:24:49 +04:00
|
|
|
package geos
|
|
|
|
|
|
|
|
/*
|
|
|
|
#cgo LDFLAGS: -lgeos_c
|
|
|
|
#include "geos_c.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
extern void goDebug(char *msg);
|
|
|
|
extern void debug_wrap(const char *fmt, ...);
|
|
|
|
extern GEOSContextHandle_t initGEOS_r_debug();
|
2013-05-15 11:49:38 +04:00
|
|
|
extern void initGEOS_debug();
|
2013-05-06 21:24:49 +04:00
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2013-05-15 11:49:38 +04:00
|
|
|
"runtime"
|
2013-05-06 21:24:49 +04:00
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
//export goDebug
|
|
|
|
func goDebug(msg *C.char) {
|
|
|
|
fmt.Println(C.GoString(msg))
|
|
|
|
}
|
|
|
|
|
|
|
|
type GEOS struct {
|
|
|
|
v C.GEOSContextHandle_t
|
|
|
|
}
|
|
|
|
|
|
|
|
type Geom struct {
|
|
|
|
v *C.GEOSGeometry
|
|
|
|
}
|
|
|
|
|
|
|
|
type CreateError string
|
|
|
|
type Error string
|
|
|
|
|
|
|
|
func (e Error) Error() string {
|
|
|
|
return string(e)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e CreateError) Error() string {
|
|
|
|
return string(e)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewGEOS() *GEOS {
|
|
|
|
geos := &GEOS{}
|
|
|
|
geos.v = C.initGEOS_r_debug()
|
|
|
|
return geos
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *GEOS) Finish() {
|
|
|
|
if this.v != nil {
|
|
|
|
C.finishGEOS_r(this.v)
|
|
|
|
this.v = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-15 11:49:38 +04:00
|
|
|
func init() {
|
|
|
|
/*
|
|
|
|
Init global GEOS handle for non _r calls.
|
|
|
|
In theory we need to always call the _r functions
|
|
|
|
with a thread/goroutine-local GEOS instance to get thread
|
|
|
|
safe behaviour. Some functions don't need a GEOS instance though
|
|
|
|
and we can make use of that e.g. to call GEOSGeom_destroy in
|
|
|
|
finalizer.
|
|
|
|
*/
|
|
|
|
C.initGEOS_debug()
|
|
|
|
}
|
|
|
|
|
2013-05-06 21:24:49 +04:00
|
|
|
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) CreatePolygon(shell *Geom, holes []*Geom) *Geom {
|
|
|
|
if len(holes) > 0 {
|
|
|
|
panic("holes not implemented")
|
|
|
|
}
|
|
|
|
polygon := C.GEOSGeom_createPolygon_r(this.v, shell.v, nil, 0)
|
|
|
|
if polygon == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &Geom{polygon}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *GEOS) GeomFromWKT(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)}
|
|
|
|
}
|
|
|
|
|
2013-05-16 14:17:21 +04:00
|
|
|
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) ExteriorRing(geom *Geom) *Geom {
|
|
|
|
ring := C.GEOSGetExteriorRing_r(this.v, geom.v)
|
|
|
|
if ring == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &Geom{ring}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *GEOS) Polygon(exterior *Geom, interiors []*Geom) *Geom {
|
|
|
|
if len(interiors) == 0 {
|
|
|
|
geom := C.GEOSGeom_createPolygon_r(this.v, exterior.v, nil, C.uint(0))
|
|
|
|
if geom == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2013-05-22 17:24:58 +04:00
|
|
|
err := C.GEOSNormalize_r(this.v, geom)
|
|
|
|
if err != 0 {
|
|
|
|
C.GEOSGeom_destroy(geom)
|
|
|
|
return nil
|
|
|
|
}
|
2013-05-16 14:17:21 +04:00
|
|
|
return &Geom{geom}
|
|
|
|
}
|
|
|
|
|
|
|
|
interiorPtr := make([]*C.GEOSGeometry, len(interiors))
|
|
|
|
for i, geom := range interiors {
|
|
|
|
interiorPtr[i] = geom.v
|
|
|
|
}
|
|
|
|
geom := C.GEOSGeom_createPolygon_r(this.v, exterior.v, &interiorPtr[0], C.uint(len(interiors)))
|
|
|
|
if geom == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2013-05-22 17:24:58 +04:00
|
|
|
err := C.GEOSNormalize_r(this.v, geom)
|
|
|
|
if err != 0 {
|
|
|
|
C.GEOSGeom_destroy(geom)
|
|
|
|
return nil
|
|
|
|
}
|
2013-05-16 14:17:21 +04:00
|
|
|
return &Geom{geom}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *GEOS) MultiPolygon(polygons []*Geom) *Geom {
|
|
|
|
polygonPtr := make([]*C.GEOSGeometry, len(polygons))
|
|
|
|
for i, geom := range polygons {
|
|
|
|
polygonPtr[i] = geom.v
|
|
|
|
}
|
|
|
|
geom := C.GEOSGeom_createCollection_r(this.v, C.GEOS_MULTIPOLYGON, &polygonPtr[0], C.uint(len(polygons)))
|
|
|
|
if geom == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &Geom{geom}
|
|
|
|
}
|
|
|
|
|
2013-05-06 21:24:49 +04:00
|
|
|
func (this *GEOS) AsWKT(geom *Geom) string {
|
2013-05-15 15:49:41 +04:00
|
|
|
str := C.GEOSGeomToWKT_r(this.v, geom.v)
|
|
|
|
result := C.GoString(str)
|
|
|
|
C.free(unsafe.Pointer(str))
|
|
|
|
return result
|
2013-05-06 21:24:49 +04:00
|
|
|
}
|
2013-05-15 15:49:41 +04:00
|
|
|
func (this *GEOS) AsWKB(geom *Geom) []byte {
|
2013-05-06 21:24:49 +04:00
|
|
|
var size C.size_t
|
2013-05-15 15:49:41 +04:00
|
|
|
buf := C.GEOSGeomToWKB_buf_r(this.v, geom.v, &size)
|
|
|
|
if buf == nil {
|
|
|
|
return nil
|
2013-05-06 21:24:49 +04:00
|
|
|
}
|
2013-05-15 15:49:41 +04:00
|
|
|
result := C.GoBytes(unsafe.Pointer(buf), C.int(size))
|
|
|
|
C.free(unsafe.Pointer(buf))
|
|
|
|
return result
|
2013-05-06 21:24:49 +04:00
|
|
|
}
|
|
|
|
|
2013-05-12 14:37:55 +04:00
|
|
|
func (this *GEOS) IsValid(geom *Geom) bool {
|
|
|
|
if C.GEOSisValid_r(this.v, geom.v) == 1 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2013-05-15 11:49:38 +04:00
|
|
|
func (this *Geom) Area() float64 {
|
2013-05-06 21:24:49 +04:00
|
|
|
var area C.double
|
2013-05-16 14:17:21 +04:00
|
|
|
if ret := C.GEOSArea(this.v, &area); ret == 1 {
|
2013-05-15 11:49:38 +04:00
|
|
|
return float64(area)
|
|
|
|
} else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *Geom) Bounds() *Bounds {
|
|
|
|
geom := C.GEOSEnvelope(this.v)
|
|
|
|
if geom == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
extRing := C.GEOSGetExteriorRing(geom)
|
|
|
|
if extRing == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
cs := C.GEOSGeom_getCoordSeq(extRing)
|
|
|
|
var csLen C.uint
|
|
|
|
C.GEOSCoordSeq_getSize(cs, &csLen)
|
|
|
|
minx := 1.e+20
|
|
|
|
maxx := -1e+20
|
|
|
|
miny := 1.e+20
|
|
|
|
maxy := -1e+20
|
|
|
|
var temp C.double
|
|
|
|
for i := 0; i < int(csLen); i++ {
|
|
|
|
C.GEOSCoordSeq_getX(cs, C.uint(i), &temp)
|
|
|
|
x := float64(temp)
|
|
|
|
if x < minx {
|
|
|
|
minx = x
|
|
|
|
}
|
|
|
|
if x > maxx {
|
|
|
|
maxx = x
|
|
|
|
}
|
|
|
|
C.GEOSCoordSeq_getY(cs, C.uint(i), &temp)
|
|
|
|
y := float64(temp)
|
|
|
|
if y < miny {
|
|
|
|
miny = y
|
|
|
|
}
|
|
|
|
if y > maxy {
|
|
|
|
maxy = y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Bounds{minx, miny, maxx, maxy}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Bounds struct {
|
|
|
|
MinX float64
|
|
|
|
MinY float64
|
|
|
|
MaxX float64
|
|
|
|
MaxY float64
|
2013-05-06 21:24:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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?")
|
|
|
|
}
|
|
|
|
}
|
2013-05-15 11:49:38 +04:00
|
|
|
|
|
|
|
func destroyGeom(geom *Geom) {
|
|
|
|
C.GEOSGeom_destroy(geom.v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *GEOS) DestroyLater(geom *Geom) {
|
|
|
|
runtime.SetFinalizer(geom, destroyGeom)
|
|
|
|
}
|
|
|
|
|
2013-05-06 21:24:49 +04:00
|
|
|
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?")
|
|
|
|
}
|
|
|
|
}
|