mirror of https://github.com/tidwall/tile38.git
roaming fence
This commit is contained in:
parent
b5b4f92d77
commit
8fdc35af61
|
@ -1,6 +1,7 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tidwall/tile38/controller/server"
|
"github.com/tidwall/tile38/controller/server"
|
||||||
|
@ -33,46 +34,63 @@ func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
match = false
|
match = false
|
||||||
|
|
||||||
|
var roamkeys, roamids []string
|
||||||
|
var roammeters []float64
|
||||||
detect := "outside"
|
detect := "outside"
|
||||||
if fence != nil {
|
if fence != nil {
|
||||||
match1 := fenceMatchObject(fence, details.oldObj)
|
if fence.roam.on {
|
||||||
match2 := fenceMatchObject(fence, details.obj)
|
if details.command == "set" {
|
||||||
if match1 && match2 {
|
// println("roam", fence.roam.key, fence.roam.id, strconv.FormatFloat(fence.roam.meters, 'f', -1, 64))
|
||||||
match = true
|
roamkeys, roamids, roammeters = fenceMatchRoam(sw.c, fence, details.key, details.id, details.obj)
|
||||||
detect = "inside"
|
|
||||||
} else if match1 && !match2 {
|
|
||||||
match = true
|
|
||||||
detect = "exit"
|
|
||||||
} else if !match1 && match2 {
|
|
||||||
match = true
|
|
||||||
detect = "enter"
|
|
||||||
if details.command == "fset" {
|
|
||||||
detect = "inside"
|
|
||||||
}
|
}
|
||||||
|
if len(roamids) == 0 || len(roamids) != len(roamkeys) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
match = true
|
||||||
|
detect = "roam"
|
||||||
} else {
|
} else {
|
||||||
if details.command != "fset" {
|
|
||||||
// Maybe the old object and new object create a line that crosses the fence.
|
// not using roaming
|
||||||
// Must detect for that possibility.
|
match1 := fenceMatchObject(fence, details.oldObj)
|
||||||
if details.oldObj != nil {
|
match2 := fenceMatchObject(fence, details.obj)
|
||||||
ls := geojson.LineString{
|
if match1 && match2 {
|
||||||
Coordinates: []geojson.Position{
|
match = true
|
||||||
details.oldObj.CalculatedPoint(),
|
detect = "inside"
|
||||||
details.obj.CalculatedPoint(),
|
} else if match1 && !match2 {
|
||||||
},
|
match = true
|
||||||
}
|
detect = "exit"
|
||||||
temp := false
|
} else if !match1 && match2 {
|
||||||
if fence.cmd == "within" {
|
match = true
|
||||||
// because we are testing if the line croses the area we need to use
|
detect = "enter"
|
||||||
// "intersects" instead of "within".
|
if details.command == "fset" {
|
||||||
fence.cmd = "intersects"
|
detect = "inside"
|
||||||
temp = true
|
}
|
||||||
}
|
} else {
|
||||||
if fenceMatchObject(fence, ls) {
|
if details.command != "fset" {
|
||||||
//match = true
|
// Maybe the old object and new object create a line that crosses the fence.
|
||||||
detect = "cross"
|
// Must detect for that possibility.
|
||||||
}
|
if details.oldObj != nil {
|
||||||
if temp {
|
ls := geojson.LineString{
|
||||||
fence.cmd = "within"
|
Coordinates: []geojson.Position{
|
||||||
|
details.oldObj.CalculatedPoint(),
|
||||||
|
details.obj.CalculatedPoint(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
temp := false
|
||||||
|
if fence.cmd == "within" {
|
||||||
|
// because we are testing if the line croses the area we need to use
|
||||||
|
// "intersects" instead of "within".
|
||||||
|
fence.cmd = "intersects"
|
||||||
|
temp = true
|
||||||
|
}
|
||||||
|
if fenceMatchObject(fence, ls) {
|
||||||
|
//match = true
|
||||||
|
detect = "cross"
|
||||||
|
}
|
||||||
|
if temp {
|
||||||
|
fence.cmd = "within"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,21 +111,22 @@ func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
||||||
sw.mu.Unlock()
|
sw.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res := sw.wr.String()
|
res := sw.wr.String()
|
||||||
resb := make([]byte, len(res))
|
resb := make([]byte, len(res))
|
||||||
copy(resb, res)
|
copy(resb, res)
|
||||||
res = string(resb)
|
|
||||||
sw.wr.Reset()
|
sw.wr.Reset()
|
||||||
|
res = string(resb)
|
||||||
if sw.output == outputIDs {
|
if sw.output == outputIDs {
|
||||||
res = `{"id":` + res + `}`
|
res = `{"id":` + res + `}`
|
||||||
}
|
}
|
||||||
sw.mu.Unlock()
|
sw.mu.Unlock()
|
||||||
|
|
||||||
if strings.HasPrefix(res, ",") {
|
if strings.HasPrefix(res, ",") {
|
||||||
res = res[1:]
|
res = res[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
jskey := jsonString(details.key)
|
jskey := jsonString(details.key)
|
||||||
|
|
||||||
ores := res
|
ores := res
|
||||||
msgs := make([]string, 0, 2)
|
msgs := make([]string, 0, 2)
|
||||||
if fence.detect == nil || fence.detect[detect] {
|
if fence.detect == nil || fence.detect[detect] {
|
||||||
|
@ -125,6 +144,15 @@ func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
||||||
if fence.detect == nil || fence.detect["outside"] {
|
if fence.detect == nil || fence.detect["outside"] {
|
||||||
msgs = append(msgs, `{"command":"`+details.command+`","detect":"outside","hook":`+jshookName+`,"key":`+jskey+`,"time":`+jstime+`,`+ores[1:])
|
msgs = append(msgs, `{"command":"`+details.command+`","detect":"outside","hook":`+jshookName+`,"key":`+jskey+`,"time":`+jstime+`,`+ores[1:])
|
||||||
}
|
}
|
||||||
|
case "roam":
|
||||||
|
if len(msgs) > 0 {
|
||||||
|
var nmsgs []string
|
||||||
|
msg := msgs[0][:len(msgs[0])-1]
|
||||||
|
for i, id := range roamids {
|
||||||
|
nmsgs = append(nmsgs, msg+`,"nearby":{"key":`+jsonString(roamkeys[i])+`,"id":`+jsonString(id)+`,"meters":`+strconv.FormatFloat(roammeters[i], 'f', -1, 64)+`}}`)
|
||||||
|
}
|
||||||
|
msgs = nmsgs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
@ -133,6 +161,10 @@ func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if fence.roam.on {
|
||||||
|
// we need to check this object against
|
||||||
|
return false
|
||||||
|
}
|
||||||
if fence.cmd == "nearby" {
|
if fence.cmd == "nearby" {
|
||||||
return obj.Nearby(geojson.Position{X: fence.lon, Y: fence.lat, Z: 0}, fence.meters)
|
return obj.Nearby(geojson.Position{X: fence.lon, Y: fence.lat, Z: 0}, fence.meters)
|
||||||
} else if fence.cmd == "within" {
|
} else if fence.cmd == "within" {
|
||||||
|
@ -154,3 +186,33 @@ func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fenceMatchRoam(c *Controller, fence *liveFenceSwitches, tkey, tid string, obj geojson.Object) (keys, ids []string, meterss []float64) {
|
||||||
|
c.mu.RLock()
|
||||||
|
defer c.mu.RUnlock()
|
||||||
|
col := c.getCol(fence.roam.key)
|
||||||
|
if col == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p := obj.CalculatedPoint()
|
||||||
|
col.Nearby(0, 0, p.Y, p.X, fence.roam.meters,
|
||||||
|
func(id string, obj geojson.Object, fields []float64) bool {
|
||||||
|
var match bool
|
||||||
|
if id == tid {
|
||||||
|
return true // skip self
|
||||||
|
}
|
||||||
|
if fence.roam.pattern {
|
||||||
|
match, _ = globMatch(fence.roam.id, id)
|
||||||
|
} else {
|
||||||
|
match = fence.roam.id == id
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
keys = append(keys, fence.roam.key)
|
||||||
|
ids = append(ids, id)
|
||||||
|
meterss = append(meterss, obj.CalculatedPoint().DistanceTo(p))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ const (
|
||||||
|
|
||||||
type scanWriter struct {
|
type scanWriter struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
c *Controller
|
||||||
wr *bytes.Buffer
|
wr *bytes.Buffer
|
||||||
msg *server.Message
|
msg *server.Message
|
||||||
col *collection.Collection
|
col *collection.Collection
|
||||||
|
@ -67,6 +68,7 @@ func (c *Controller) newScanWriter(
|
||||||
case outputIDs, outputObjects, outputCount, outputBounds, outputPoints, outputHashes:
|
case outputIDs, outputObjects, outputCount, outputBounds, outputPoints, outputHashes:
|
||||||
}
|
}
|
||||||
sw := &scanWriter{
|
sw := &scanWriter{
|
||||||
|
c: c,
|
||||||
wr: wr,
|
wr: wr,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
output: output,
|
output: output,
|
||||||
|
|
|
@ -20,6 +20,15 @@ type liveFenceSwitches struct {
|
||||||
minLat, minLon float64
|
minLat, minLon float64
|
||||||
maxLat, maxLon float64
|
maxLat, maxLon float64
|
||||||
cmd string
|
cmd string
|
||||||
|
roam roamSwitches
|
||||||
|
}
|
||||||
|
|
||||||
|
type roamSwitches struct {
|
||||||
|
on bool
|
||||||
|
key string
|
||||||
|
id string
|
||||||
|
pattern bool
|
||||||
|
meters float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s liveFenceSwitches) Error() string {
|
func (s liveFenceSwitches) Error() string {
|
||||||
|
@ -46,18 +55,23 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ltyp := strings.ToLower(typ)
|
||||||
var found bool
|
var found bool
|
||||||
for _, t := range types {
|
for _, t := range types {
|
||||||
if strings.ToLower(typ) == t {
|
if ltyp == t {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !found && s.searchScanBaseTokens.fence && ltyp == "roam" && cmd == "nearby" {
|
||||||
|
// allow roaming for nearby fence searches.
|
||||||
|
found = true
|
||||||
|
}
|
||||||
if !found {
|
if !found {
|
||||||
err = errInvalidArgument(typ)
|
err = errInvalidArgument(typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch strings.ToLower(typ) {
|
switch ltyp {
|
||||||
case "point":
|
case "point":
|
||||||
var slat, slon, smeters string
|
var slat, slon, smeters string
|
||||||
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
|
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
|
||||||
|
@ -206,6 +220,26 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
||||||
} else {
|
} else {
|
||||||
s.o = o
|
s.o = o
|
||||||
}
|
}
|
||||||
|
case "roam":
|
||||||
|
s.roam.on = true
|
||||||
|
if vs, s.roam.key, ok = tokenval(vs); !ok || s.roam.key == "" {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if vs, s.roam.id, ok = tokenval(vs); !ok || s.roam.id == "" {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.roam.pattern = globIsGlob(s.roam.id)
|
||||||
|
var smeters string
|
||||||
|
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.roam.meters, err = strconv.ParseFloat(smeters, 64); err != nil {
|
||||||
|
err = errInvalidArgument(smeters)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(vs) != 0 {
|
if len(vs) != 0 {
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
|
|
Loading…
Reference in New Issue