Skip to content
This repository has been archived by the owner on Sep 19, 2023. It is now read-only.

Add strtree implementation #17

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
/_site
/tags
.idea
2 changes: 1 addition & 1 deletion geos/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: fmt check

SRCS = coord.go coordseq.go cwrappers.go geom.go geos.c geos.go geos.h helper.go prepared.go types.go wkb.go wkt.go
SRCS = coord.go coordseq.go cwrappers.go geom.go geos.c geos.go geos.h helper.go prepared.go strtree.go types.go wkb.go wkt.go

all: build

Expand Down
2 changes: 1 addition & 1 deletion geos/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package geos_test
import (
"fmt"

"github.com/paulsmith/gogeos/geos"
"github.com/robertogyn19/gogeos/geos"
)

func ExampleGeometry_LineInterpolatePoint() {
Expand Down
62 changes: 62 additions & 0 deletions geos/examples/strtree/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"encoding/json"
"fmt"

"github.com/robertogyn19/gogeos/geos"
)

// Callback used to get items from query/iterate functions
func handleResult(item []byte) {
i := Item{}
json.Unmarshal(item, &i)

fmt.Printf("Item: %v\n", i)
}

type Item struct {
Index int
Name string
Price float64
}

/**
Implement geos.STRTreeItem interface to be able to insert
*/
func (i Item) Parse() []byte {
j, _ := json.Marshal(i)
return j
}

func main() {
strTreeCapacity := 10
strtree := geos.NewSTRTree(strTreeCapacity)

points := []geos.Coord{geos.NewCoord(-46, -23)}

for i := 1; i < strTreeCapacity; i++ {
points = append(points, geos.NewCoord(0, 0))
}

array := []geos.STRTreeItem{}
for i := 0; i < strTreeCapacity; i++ {
name := fmt.Sprintf("Product-%d", i+1)
p := float64((i + 1) * 10)
array = append(array, Item{i + 1, name, p})
}

for i, item := range array {
g, _ := geos.NewPoint(points[i])
strtree.Insert(g, item)
}

wktPolygon := "POLYGON ((-46.19064331 -23.15930069, -46.19064331 -22.79580631, -45.81367493 -22.79580631, -45.81367493 -23.15930069, -46.19064331 -23.15930069))"
polygon, _ := geos.FromWKT(wktPolygon)

fmt.Println("Query with polygon (should print only one item):")
strtree.Query(polygon, handleResult)

fmt.Println("\nIterate all items (should iterate all items):")
strtree.Iterate(handleResult)
}
4 changes: 4 additions & 0 deletions geos/geos.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ char *gogeos_get_last_error(void) {
GEOSContextHandle_t gogeos_initGEOS() {
return initGEOS_r(gogeos_notice_handler, gogeos_error_handler);
}

void strTreeQueryCallbackGo(void *item, void *data) {
StrTreeQueryCallbackGo(item, data);
}
3 changes: 3 additions & 0 deletions geos/geos.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ void gogeos_notice_handler(const char *fmt, ...);
void gogeos_error_handler(const char *fmt, ...);
char *gogeos_get_last_error(void);
GEOSContextHandle_t gogeos_initGEOS();

typedef void (*strTreeQueryCallback)(void*, void*);
void strTreeQueryCallbackGo(void *item, void *data);
2 changes: 1 addition & 1 deletion geos/geos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

func TestVersion(t *testing.T) {
const re = `3\.3\.\d+-CAPI-1\.7\.\d+$`
const re = `3\.[3-4]\.\d+-CAPI-1\.[7-8]\.\d+.*`
version := Version()
matched, err := regexp.MatchString(re, version)
if err != nil {
Expand Down
126 changes: 126 additions & 0 deletions geos/strtree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package geos

/*
#include "geos.h"
*/
import "C"

import (
"fmt"
"runtime"
"unsafe"
"sync"
)

type STRTreeCallbackFunc func(item []byte)
type STRTreeItem interface {
Parse() []byte
}

type strTree struct {
r *C.GEOSSTRtree
cb C.GEOSQueryCallback
}

func NewSTRTree(capacity int) *strTree {
r := cGEOSSTRtree_create(C.size_t(capacity))

if r == nil {
return nil
}

strtree := &strTree{r, NewCallback()}
runtime.SetFinalizer(strtree, (*strTree).destroy)

return strtree
}

func (tree *strTree) Insert(g *Geometry, item STRTreeItem) {
bstr := item.Parse()
cstr := C.CString(string(bstr))
p := (*C.void)(unsafe.Pointer(cstr))

cGEOSSTRtree_insert(tree.r, g.g, p)
}

func (tree *strTree) Query(g *Geometry, cb STRTreeCallbackFunc) {
cbid := register(cb)
ccbid := C.int(cbid)
cGEOSSTRtree_query(tree.r, g.g, tree.cb, (*C.void)(unsafe.Pointer(&ccbid)))
defer unregister(cbid)
}

func (tree *strTree) Iterate(cb STRTreeCallbackFunc) {
cbid := register(cb)
ccbid := C.int(cbid)
cGEOSSTRtree_iterate(tree.r, tree.cb, (*C.void)(unsafe.Pointer(&ccbid)))
defer unregister(cbid)
}

func (tree *strTree) destroy() {
cGEOSSTRtree_destroy(tree.r)
tree.r = nil
}

func NewCallback() C.GEOSQueryCallback {
return NewCustomCallback(unsafe.Pointer(C.strTreeQueryCallbackGo))
}

func NewCustomCallback(pointer unsafe.Pointer) C.GEOSQueryCallback {
return (C.GEOSQueryCallback)(pointer)
}

/* ------------------------------------------------------------------------- */
/* */
/* Callback functions */
/* */
/* ------------------------------------------------------------------------- */

var mu sync.Mutex
var fns = make(map[int]STRTreeCallbackFunc)
var cbIndex int

//export StrTreeQueryCallbackGo
func StrTreeQueryCallbackGo(item unsafe.Pointer, data unsafe.Pointer) {
s := itemBytes(item)

if data == nil {
fmt.Println("Callback data not found. Forgot to register a callback?")
return
}

ccbid := *(*C.int)(data)
cbid := int(ccbid)
cb := lookup(cbid)
cb(s)
}

func itemBytes(item unsafe.Pointer) []byte {
value1 := (*C.char)(item)
return []byte(C.GoString(value1))
}

func register(fn STRTreeCallbackFunc) int {
mu.Lock()
defer mu.Unlock()
cbIndex++

for fns[cbIndex] != nil {
cbIndex++
}

fns[cbIndex] = fn
return cbIndex
}

func lookup(i int) STRTreeCallbackFunc {
mu.Lock()
defer mu.Unlock()
return fns[i]
}

func unregister(i int) {
mu.Lock()
defer mu.Unlock()
delete(fns, i)
}
45 changes: 45 additions & 0 deletions geos/strtree_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package geos

import (
"testing"
)

func TestNewSTRTree(t *testing.T) {
tree := NewSTRTree(10)

if tree == nil {
t.Error("STRTree should not be nil")
}
}

type itemTest string

func (i itemTest) Parse() []byte {
return []byte(i)
}

func TestStrTree_Insert(t *testing.T) {
tree := NewSTRTree(10)

item := itemTest("test1")
g, _ := FromWKT("POINT(-46 -23)")
tree.Insert(g, item)

items := 0

cb := func(bitem []byte) {
items++

if items != 1 {
t.Error("Should have only one item")
}

if itemTest(bitem) != item {
t.Errorf("Expect item: %s, got %s", item, bitem)
}
}

tree.Iterate(cb)
}

// TODO Improve tests