imposm3/geom/geos/geos.go

526 lines
12 KiB
Go
Raw Normal View History

2013-05-06 21:24:49 +04:00
package geos
/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
2013-05-23 19:53:58 +04:00
extern void goLogString(char *msg);
2013-05-06 21:24:49 +04:00
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-24 14:28:22 +04:00
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);
2013-05-06 21:24:49 +04:00
*/
import "C"
import (
2013-05-23 19:53:58 +04:00
"goposm/logging"
2013-05-15 11:49:38 +04:00
"runtime"
2013-05-06 21:24:49 +04:00
"unsafe"
)
2013-05-23 19:53:58 +04:00
var log = logging.NewLogger("GEOS")
//export goLogString
func goLogString(msg *C.char) {
log.Printf(C.GoString(msg))
2013-05-06 21:24:49 +04:00
}
type Geos struct {
2013-05-06 21:24:49 +04:00
v C.GEOSContextHandle_t
}
type Geom struct {
v *C.GEOSGeometry
}
type PreparedGeom struct {
v *C.GEOSPreparedGeometry
}
2013-05-06 21:24:49 +04:00
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{}
2013-05-06 21:24:49 +04:00
geos.v = C.initGEOS_r_debug()
return geos
}
func (this *Geos) Finish() {
2013-05-06 21:24:49 +04:00
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) {
2013-05-06 21:24:49 +04:00
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 {
2013-05-06 21:24:49 +04:00
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) {
2013-05-06 21:24:49 +04:00
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) {
2013-05-06 21:24:49 +04:00
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) {
2013-05-06 21:24:49 +04:00
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 {
2013-05-06 21:24:49 +04:00
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) FromWkt(wkt string) (geom *Geom) {
2013-05-06 21:24:49 +04:00
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 {
2013-05-06 21:24:49 +04:00
return &Geom{C.GEOSBuffer_r(this.v, geom.v, C.double(size), 50)}
}
2013-05-27 17:26:42 +04:00
func (this *Geos) NumGeoms(geom *Geom) int32 {
count := int32(C.GEOSGetNumGeometries_r(this.v, geom.v))
return count
}
func (this *Geos) Geoms(geom *Geom) []*Geom {
count := this.NumGeoms(geom)
var result []*Geom
for i := 0; int32(i) < count; i++ {
part := C.GEOSGetGeometryN_r(this.v, geom.v, C.int(i))
if part == nil {
return nil
}
result = append(result, &Geom{part})
}
return result
}
func (this *Geos) Contains(a, b *Geom) bool {
2013-05-16 14:17:21 +04:00
result := C.GEOSContains_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
2013-05-27 17:26:42 +04:00
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 {
result := C.GEOSPreparedIntersects_r(this.v, a.v, b.v)
if result == 1 {
return true
}
// result == 2 -> exception (already logged to console)
return false
}
2013-05-27 17:26:42 +04:00
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)
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)
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 {
2013-05-16 14:17:21 +04:00
ring := C.GEOSGetExteriorRing_r(this.v, geom.v)
if ring == nil {
return nil
}
return &Geom{ring}
}
2013-05-27 17:26:42 +04:00
func (this *Geos) BoundsPolygon(bounds Bounds) *Geom {
coordSeq, err := this.CreateCoordSeq(5, 2)
if err != nil {
return nil
}
// coordSeq inherited by LineString, no destroy
if err := coordSeq.SetXY(this, 0, bounds.MinX, bounds.MinY); err != nil {
return nil
}
if err := coordSeq.SetXY(this, 1, bounds.MaxX, bounds.MinY); err != nil {
return nil
}
if err := coordSeq.SetXY(this, 2, bounds.MaxX, bounds.MaxY); err != nil {
return nil
}
if err := coordSeq.SetXY(this, 3, bounds.MinX, bounds.MaxY); err != nil {
return nil
}
if err := coordSeq.SetXY(this, 4, bounds.MinX, bounds.MinY); err != nil {
return nil
}
geom, err := coordSeq.AsLinearRing(this)
if err != nil {
return nil
}
// geom inherited by Polygon, no destroy
geom = this.CreatePolygon(geom, nil)
this.DestroyLater(geom)
return geom
}
func (this *Geos) Polygon(exterior *Geom, interiors []*Geom) *Geom {
2013-05-16 14:17:21 +04:00
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 {
2013-05-16 14:17:21 +04:00
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-27 17:26:42 +04:00
func (this *Geos) MultiLineString(lines []*Geom) *Geom {
linePtr := make([]*C.GEOSGeometry, len(lines))
for i, geom := range lines {
linePtr[i] = geom.v
}
geom := C.GEOSGeom_createCollection_r(this.v, C.GEOS_MULTILINESTRING, &linePtr[0], C.uint(len(lines)))
if geom == nil {
return nil
}
return &Geom{geom}
}
2013-05-16 14:17:21 +04:00
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
2013-05-06 21:24:49 +04:00
}
func (this *Geos) AsWkb(geom *Geom) []byte {
2013-05-06 21:24:49 +04:00
var size C.size_t
buf := C.GEOSGeomToWKB_buf_r(this.v, geom.v, &size)
if buf == nil {
return nil
2013-05-06 21:24:49 +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
}
func (this *Geos) FromWkb(wkb []byte) *Geom {
2013-05-24 12:03:31 +04:00
geom := C.GEOSGeomFromWKB_buf((*C.uchar)(&wkb[0]), C.size_t(len(wkb)))
if geom == nil {
return nil
}
return &Geom{geom}
}
func (this *Geos) IsValid(geom *Geom) bool {
2013-05-12 14:37:55 +04:00
if C.GEOSisValid_r(this.v, geom.v) == 1 {
return true
}
return false
}
2013-05-27 17:26:42 +04:00
func (this *Geos) IsEmpty(geom *Geom) bool {
if C.GEOSisEmpty_r(this.v, geom.v) == 1 {
return true
}
return false
}
func (this *Geos) Type(geom *Geom) string {
geomType := C.GEOSGeomType_r(this.v, geom.v)
if geomType == nil {
return "Unknown"
}
defer C.free(unsafe.Pointer(geomType))
return C.GoString(geomType)
}
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
}
}
2013-05-27 17:26:42 +04:00
func (this *Geom) Length() float64 {
var length C.double
if ret := C.GEOSLength(this.v, &length); ret == 1 {
return float64(length)
} else {
return 0
}
}
var NilBounds = Bounds{1e20, 1e20, -1e20, -1e20}
func (this *Geom) Bounds() Bounds {
2013-05-15 11:49:38 +04:00
geom := C.GEOSEnvelope(this.v)
if geom == nil {
2013-05-27 17:26:42 +04:00
return NilBounds
2013-05-15 11:49:38 +04:00
}
extRing := C.GEOSGetExteriorRing(geom)
if extRing == nil {
2013-05-27 17:26:42 +04:00
return NilBounds
2013-05-15 11:49:38 +04:00
}
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
}
}
2013-05-27 17:26:42 +04:00
return Bounds{minx, miny, maxx, maxy}
2013-05-15 11:49:38 +04:00
}
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) {
2013-05-06 21:24:49 +04:00
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) {
2013-05-15 11:49:38 +04:00
runtime.SetFinalizer(geom, destroyGeom)
}
func (this *Geos) DestroyCoordSeq(coordSeq *CoordSeq) {
2013-05-06 21:24:49 +04:00
if coordSeq.v != nil {
C.GEOSCoordSeq_destroy_r(this.v, coordSeq.v)
coordSeq.v = nil
} else {
panic("double free?")
}
}
2013-05-24 14:28:22 +04:00
type indexGeom struct {
Geom *Geom
Prepared *PreparedGeom
}
2013-05-24 14:28:22 +04:00
type Index struct {
2013-05-27 17:26:42 +04:00
v *C.GEOSSTRtree
geoms []indexGeom
2013-05-24 14:28:22 +04:00
}
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{}}
2013-05-24 14:28:22 +04:00
}
// IndexQuery adds a geom to the index with the id.
2013-05-27 17:26:42 +04:00
func (this *Geos) IndexAdd(index *Index, geom *Geom) {
id := len(index.geoms)
2013-05-24 14:28:22 +04:00
C.IndexAdd(this.v, index.v, geom.v, C.size_t(id))
prep := this.Prepare(geom)
index.geoms = append(index.geoms, indexGeom{geom, prep})
2013-05-24 14:28:22 +04:00
}
2013-05-27 17:26:42 +04:00
// IndexQuery queries the index for intersections with geom.
func (this *Geos) IndexQuery(index *Index, geom *Geom) []indexGeom {
2013-05-24 14:28:22 +04:00
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
2013-05-27 17:26:42 +04:00
for idx := range hits {
geoms = append(geoms, index.geoms[idx])
}
return geoms
2013-05-24 14:28:22 +04:00
}
//export goIndexSendQueryResult
func goIndexSendQueryResult(id C.size_t, ptr unsafe.Pointer) {
results := *(*chan int)(ptr)
results <- int(id)
}