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
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tidwall/tile38/controller/server"
|
||||
|
@ -33,46 +34,63 @@ func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
|
|||
return nil
|
||||
}
|
||||
match = false
|
||||
|
||||
var roamkeys, roamids []string
|
||||
var roammeters []float64
|
||||
detect := "outside"
|
||||
if fence != nil {
|
||||
match1 := fenceMatchObject(fence, details.oldObj)
|
||||
match2 := fenceMatchObject(fence, details.obj)
|
||||
if match1 && match2 {
|
||||
match = true
|
||||
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 fence.roam.on {
|
||||
if details.command == "set" {
|
||||
// println("roam", fence.roam.key, fence.roam.id, strconv.FormatFloat(fence.roam.meters, 'f', -1, 64))
|
||||
roamkeys, roamids, roammeters = fenceMatchRoam(sw.c, fence, details.key, details.id, details.obj)
|
||||
}
|
||||
if len(roamids) == 0 || len(roamids) != len(roamkeys) {
|
||||
return nil
|
||||
}
|
||||
match = true
|
||||
detect = "roam"
|
||||
} else {
|
||||
if details.command != "fset" {
|
||||
// Maybe the old object and new object create a line that crosses the fence.
|
||||
// Must detect for that possibility.
|
||||
if details.oldObj != nil {
|
||||
ls := geojson.LineString{
|
||||
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"
|
||||
|
||||
// not using roaming
|
||||
match1 := fenceMatchObject(fence, details.oldObj)
|
||||
match2 := fenceMatchObject(fence, details.obj)
|
||||
if match1 && match2 {
|
||||
match = true
|
||||
detect = "inside"
|
||||
} else if match1 && !match2 {
|
||||
match = true
|
||||
detect = "exit"
|
||||
} else if !match1 && match2 {
|
||||
match = true
|
||||
detect = "enter"
|
||||
if details.command == "fset" {
|
||||
detect = "inside"
|
||||
}
|
||||
} else {
|
||||
if details.command != "fset" {
|
||||
// Maybe the old object and new object create a line that crosses the fence.
|
||||
// Must detect for that possibility.
|
||||
if details.oldObj != nil {
|
||||
ls := geojson.LineString{
|
||||
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()
|
||||
return nil
|
||||
}
|
||||
|
||||
res := sw.wr.String()
|
||||
resb := make([]byte, len(res))
|
||||
copy(resb, res)
|
||||
res = string(resb)
|
||||
sw.wr.Reset()
|
||||
res = string(resb)
|
||||
if sw.output == outputIDs {
|
||||
res = `{"id":` + res + `}`
|
||||
}
|
||||
sw.mu.Unlock()
|
||||
|
||||
if strings.HasPrefix(res, ",") {
|
||||
res = res[1:]
|
||||
}
|
||||
|
||||
jskey := jsonString(details.key)
|
||||
|
||||
ores := res
|
||||
msgs := make([]string, 0, 2)
|
||||
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"] {
|
||||
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
|
||||
}
|
||||
|
@ -133,6 +161,10 @@ func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
|
|||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
if fence.roam.on {
|
||||
// we need to check this object against
|
||||
return false
|
||||
}
|
||||
if fence.cmd == "nearby" {
|
||||
return obj.Nearby(geojson.Position{X: fence.lon, Y: fence.lat, Z: 0}, fence.meters)
|
||||
} else if fence.cmd == "within" {
|
||||
|
@ -154,3 +186,33 @@ func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
|
|||
}
|
||||
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 {
|
||||
mu sync.Mutex
|
||||
c *Controller
|
||||
wr *bytes.Buffer
|
||||
msg *server.Message
|
||||
col *collection.Collection
|
||||
|
@ -67,6 +68,7 @@ func (c *Controller) newScanWriter(
|
|||
case outputIDs, outputObjects, outputCount, outputBounds, outputPoints, outputHashes:
|
||||
}
|
||||
sw := &scanWriter{
|
||||
c: c,
|
||||
wr: wr,
|
||||
msg: msg,
|
||||
output: output,
|
||||
|
|
|
@ -20,6 +20,15 @@ type liveFenceSwitches struct {
|
|||
minLat, minLon float64
|
||||
maxLat, maxLon float64
|
||||
cmd string
|
||||
roam roamSwitches
|
||||
}
|
||||
|
||||
type roamSwitches struct {
|
||||
on bool
|
||||
key string
|
||||
id string
|
||||
pattern bool
|
||||
meters float64
|
||||
}
|
||||
|
||||
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
|
||||
for _, t := range types {
|
||||
if strings.ToLower(typ) == t {
|
||||
if ltyp == t {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found && s.searchScanBaseTokens.fence && ltyp == "roam" && cmd == "nearby" {
|
||||
// allow roaming for nearby fence searches.
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
err = errInvalidArgument(typ)
|
||||
return
|
||||
}
|
||||
switch strings.ToLower(typ) {
|
||||
switch ltyp {
|
||||
case "point":
|
||||
var slat, slon, smeters string
|
||||
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
|
||||
|
@ -206,6 +220,26 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string)
|
|||
} else {
|
||||
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 {
|
||||
err = errInvalidNumberOfArguments
|
||||
|
|
Loading…
Reference in New Issue