Merge branch 'iwpnd-feat/add-sector-area'

This commit is contained in:
tidwall 2021-09-29 07:20:27 -07:00
commit ad6e5aeab8
16 changed files with 899 additions and 244 deletions

View File

@ -919,6 +919,31 @@
"type": "geohash" "type": "geohash"
} }
] ]
},
{
"name": "SECTOR",
"arguments": [
{
"name": "lat",
"type": "double"
},
{
"name": "lon",
"type": "double"
},
{
"name": "radius",
"type": "double"
},
{
"name": "startBearing",
"type": "double"
},
{
"name": "endBearing",
"type": "double"
}
]
} }
] ]
} }
@ -1146,6 +1171,31 @@
"type": "geohash" "type": "geohash"
} }
] ]
},
{
"name": "SECTOR",
"arguments": [
{
"name": "lat",
"type": "double"
},
{
"name": "lon",
"type": "double"
},
{
"name": "radius",
"type": "double"
},
{
"name": "startBearing",
"type": "double"
},
{
"name": "endBearing",
"type": "double"
}
]
} }
] ]
} }
@ -1373,7 +1423,6 @@
"type": "string", "type": "string",
"variadic": true "variadic": true
} }
], ],
"group": "webhook" "group": "webhook"
}, },
@ -1458,7 +1507,6 @@
"type": "string", "type": "string",
"variadic": true "variadic": true
} }
], ],
"group": "pubsub" "group": "pubsub"
}, },

View File

@ -1085,6 +1085,31 @@ var commandsJSON = `{
"type": "geohash" "type": "geohash"
} }
] ]
},
{
"name": "SECTOR",
"arguments": [
{
"name": "lat",
"type": "double"
},
{
"name": "lon",
"type": "double"
},
{
"name": "radius",
"type": "double"
},
{
"name": "startBearing",
"type": "double"
},
{
"name": "endBearing",
"type": "double"
}
]
} }
] ]
} }
@ -1312,6 +1337,31 @@ var commandsJSON = `{
"type": "geohash" "type": "geohash"
} }
] ]
},
{
"name": "SECTOR",
"arguments": [
{
"name": "lat",
"type": "double"
},
{
"name": "lon",
"type": "double"
},
{
"name": "radius",
"type": "double"
},
{
"name": "startBearing",
"type": "double"
},
{
"name": "endBearing",
"type": "double"
}
]
} }
] ]
} }
@ -1539,7 +1589,6 @@ var commandsJSON = `{
"type": "string", "type": "string",
"variadic": true "variadic": true
} }
], ],
"group": "webhook" "group": "webhook"
}, },
@ -1624,7 +1673,6 @@ var commandsJSON = `{
"type": "string", "type": "string",
"variadic": true "variadic": true
} }
], ],
"group": "pubsub" "group": "pubsub"
}, },

1
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/eclipse/paho.mqtt.golang v1.3.1 github.com/eclipse/paho.mqtt.golang v1.3.1
github.com/golang/protobuf v1.4.3 github.com/golang/protobuf v1.4.3
github.com/gomodule/redigo v1.8.3 github.com/gomodule/redigo v1.8.3
github.com/iwpnd/sectr v0.1.2
github.com/mmcloughlin/geohash v0.10.0 github.com/mmcloughlin/geohash v0.10.0
github.com/nats-io/nats-server/v2 v2.2.0 // indirect github.com/nats-io/nats-server/v2 v2.2.0 // indirect
github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac

2
go.sum
View File

@ -214,6 +214,8 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/iwpnd/sectr v0.1.2 h1:FauaPRn5C2tC42HTF7gM3FJZXvGXWc6jabBbIxzTMag=
github.com/iwpnd/sectr v0.1.2/go.mod h1:Dm6YXDJCRx1NTfMX/1RMIkGfVp2ORjCY/cQGfbknz4c=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=

View File

@ -3,10 +3,12 @@ package server
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/iwpnd/sectr"
"github.com/mmcloughlin/geohash" "github.com/mmcloughlin/geohash"
"github.com/tidwall/geojson" "github.com/tidwall/geojson"
"github.com/tidwall/geojson/geometry" "github.com/tidwall/geojson/geometry"
@ -280,6 +282,66 @@ func (server *Server) cmdSearchArgs(
if err != nil { if err != nil {
return return
} }
case "sector":
if s.clip {
err = errInvalidArgument("cannot clip with " + ltyp)
return
}
var slat, slon, smeters, sb1, sb2 string
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
err = errInvalidNumberOfArguments
return
}
if vs, slon, ok = tokenval(vs); !ok || slon == "" {
err = errInvalidNumberOfArguments
return
}
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
err = errInvalidNumberOfArguments
return
}
if vs, sb1, ok = tokenval(vs); !ok || sb1 == "" {
err = errInvalidNumberOfArguments
return
}
if vs, sb2, ok = tokenval(vs); !ok || sb2 == "" {
err = errInvalidNumberOfArguments
return
}
var lat, lon, meters, b1, b2 float64
if lat, err = strconv.ParseFloat(slat, 64); err != nil {
err = errInvalidArgument(slat)
return
}
if lon, err = strconv.ParseFloat(slon, 64); err != nil {
err = errInvalidArgument(slon)
return
}
if meters, err = strconv.ParseFloat(smeters, 64); err != nil {
err = errInvalidArgument(smeters)
return
}
if b1, err = strconv.ParseFloat(sb1, 64); err != nil {
err = errInvalidArgument(sb1)
return
}
if b2, err = strconv.ParseFloat(sb2, 64); err != nil {
err = errInvalidArgument(sb2)
return
}
if b1 == b2 {
err = fmt.Errorf("equal bearings (%s == %s), use CIRCLE instead", sb1, sb2)
return
}
origin := sectr.Point{Lng: lon, Lat: lat}
sector := sectr.NewSector(origin, meters, b1, b2)
s.obj, err = geojson.Parse(string(sector.JSON()), &server.geomParseOpts)
if err != nil {
return
}
case "bounds", "hash", "tile", "quadkey": case "bounds", "hash", "tile", "quadkey":
vs, s.obj, err = parseRectArea(ltyp, vs) vs, s.obj, err = parseRectArea(ltyp, vs)
if err != nil { if err != nil {
@ -379,7 +441,7 @@ func (server *Server) cmdSearchArgs(
var nearbyTypes = []string{"point"} var nearbyTypes = []string{"point"}
var withinOrIntersectsTypes = []string{ var withinOrIntersectsTypes = []string{
"geo", "bounds", "hash", "tile", "quadkey", "get", "object", "circle"} "geo", "bounds", "hash", "tile", "quadkey", "get", "object", "circle", "sector"}
func (server *Server) cmdNearby(msg *Message) (res resp.Value, err error) { func (server *Server) cmdNearby(msg *Message) (res resp.Value, err error) {
start := time.Now() start := time.Now()

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/iwpnd/sectr"
"github.com/mmcloughlin/geohash" "github.com/mmcloughlin/geohash"
"github.com/tidwall/geojson" "github.com/tidwall/geojson"
"github.com/tidwall/geojson/geometry" "github.com/tidwall/geojson/geometry"
@ -47,6 +48,67 @@ func (s *Server) parseArea(ovs []string, doClip bool) (vs []string, o geojson.Ob
return return
} }
o = geojson.NewPoint(geometry.Point{X: lon, Y: lat}) o = geojson.NewPoint(geometry.Point{X: lon, Y: lat})
case "sector":
if doClip {
err = errInvalidArgument("cannot clip with " + ltyp)
return
}
var slat, slon, smeters, sb1, sb2 string
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
err = errInvalidNumberOfArguments
return
}
if vs, slon, ok = tokenval(vs); !ok || slon == "" {
err = errInvalidNumberOfArguments
return
}
if vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
err = errInvalidNumberOfArguments
return
}
if vs, sb1, ok = tokenval(vs); !ok || sb1 == "" {
err = errInvalidNumberOfArguments
return
}
if vs, sb2, ok = tokenval(vs); !ok || sb2 == "" {
err = errInvalidNumberOfArguments
return
}
var lat, lon, meters, b1, b2 float64
if lat, err = strconv.ParseFloat(slat, 64); err != nil {
err = errInvalidArgument(slat)
return
}
if lon, err = strconv.ParseFloat(slon, 64); err != nil {
err = errInvalidArgument(slon)
return
}
if meters, err = strconv.ParseFloat(smeters, 64); err != nil {
err = errInvalidArgument(smeters)
return
}
if b1, err = strconv.ParseFloat(sb1, 64); err != nil {
err = errInvalidArgument(sb1)
return
}
if b2, err = strconv.ParseFloat(sb2, 64); err != nil {
err = errInvalidArgument(sb2)
return
}
if b1 == b2 {
err = fmt.Errorf("equal bearings (%s == %s), use CIRCLE instead", sb1, sb2)
return
}
origin := sectr.Point{Lng: lon, Lat: lat}
sector := sectr.NewSector(origin, meters, b1, b2)
o, err = geojson.Parse(string(sector.JSON()), &s.geomParseOpts)
if err != nil {
return
}
case "circle": case "circle":
if doClip { if doClip {
err = fmt.Errorf("invalid clip type '%s'", typ) err = fmt.Errorf("invalid clip type '%s'", typ)

View File

@ -763,7 +763,7 @@ loop:
ae = &areaExpression{op: OR, children: []*areaExpression{ae}} ae = &areaExpression{op: OR, children: []*areaExpression{ae}}
} }
vsout = nvs vsout = nvs
case "point", "circle", "object", "bounds", "hash", "quadkey", "tile", "get": case "point", "circle", "object", "bounds", "hash", "quadkey", "tile", "get", "sector":
parsedVs, parsedObj, areaErr := s.parseArea(vsout, doClip) parsedVs, parsedObj, areaErr := s.parseArea(vsout, doClip)
if areaErr != nil { if areaErr != nil {
err = areaErr err = areaErr

View File

@ -19,7 +19,9 @@ func subTestSearch(t *testing.T, mc *mockServer) {
runStep(t, mc, "KNN_CURSOR", keys_KNN_cursor_test) runStep(t, mc, "KNN_CURSOR", keys_KNN_cursor_test)
runStep(t, mc, "NEARBY_SPARSE", keys_NEARBY_SPARSE_test) runStep(t, mc, "NEARBY_SPARSE", keys_NEARBY_SPARSE_test)
runStep(t, mc, "WITHIN_CIRCLE", keys_WITHIN_CIRCLE_test) runStep(t, mc, "WITHIN_CIRCLE", keys_WITHIN_CIRCLE_test)
runStep(t, mc, "WITHIN_SECTOR", keys_WITHIN_SECTOR_test)
runStep(t, mc, "INTERSECTS_CIRCLE", keys_INTERSECTS_CIRCLE_test) runStep(t, mc, "INTERSECTS_CIRCLE", keys_INTERSECTS_CIRCLE_test)
runStep(t, mc, "INTERSECTS_SECTOR", keys_INTERSECTS_SECTOR_test)
runStep(t, mc, "WITHIN", keys_WITHIN_test) runStep(t, mc, "WITHIN", keys_WITHIN_test)
runStep(t, mc, "WITHIN_CURSOR", keys_WITHIN_CURSOR_test) runStep(t, mc, "WITHIN_CURSOR", keys_WITHIN_CURSOR_test)
runStep(t, mc, "WITHIN_CLIPBY", keys_WITHIN_CLIPBY_test) runStep(t, mc, "WITHIN_CLIPBY", keys_WITHIN_CLIPBY_test)
@ -144,6 +146,8 @@ func keys_WITHIN_test(mc *mockServer) error {
] ]
] ]
}`}, {"[0 [point1 point2 line3 poly4 multipoly5 poly8]]"}, }`}, {"[0 [point1 point2 line3 poly4 multipoly5 poly8]]"},
{"WITHIN", "mykey", "IDS", "SECTOR", "37.72999", "-122.44760", "1000", "0", "90"}, {"[0 [point1 point2 line3 poly4 multipoly5 poly8]]"},
{"WITHIN", "mykey", "IDS", "SECTOR", "37.72999", "-122.44760", "1000", "0", "0"}, {"ERR equal bearings (0 == 0), use CIRCLE instead"},
{"SET", "key2", "poly9", "OBJECT", `{"type":"Polygon","coordinates":[[[-122.44037926197052,37.73313523548048],[-122.44017541408539,37.73313523548048],[-122.44017541408539,37.73336857568778],[-122.44037926197052,37.73336857568778],[-122.44037926197052,37.73313523548048]]]}`}, {"OK"}, {"SET", "key2", "poly9", "OBJECT", `{"type":"Polygon","coordinates":[[[-122.44037926197052,37.73313523548048],[-122.44017541408539,37.73313523548048],[-122.44017541408539,37.73336857568778],[-122.44037926197052,37.73336857568778],[-122.44037926197052,37.73313523548048]]]}`}, {"OK"},
{"SET", "key2", "poly10", "OBJECT", `{"type":"Polygon","coordinates":[[[-122.44040071964262,37.73359343010089],[-122.4402666091919,37.73359343010089],[-122.4402666091919,37.73373767596864],[-122.44040071964262,37.73373767596864],[-122.44040071964262,37.73359343010089]]]}`}, {"OK"}, {"SET", "key2", "poly10", "OBJECT", `{"type":"Polygon","coordinates":[[[-122.44040071964262,37.73359343010089],[-122.4402666091919,37.73359343010089],[-122.4402666091919,37.73373767596864],[-122.44040071964262,37.73373767596864],[-122.44040071964262,37.73359343010089]]]}`}, {"OK"},
@ -203,6 +207,8 @@ func keys_WITHIN_CURSOR_test(mc *mockServer) error {
"[6 [poly8]]"}, "[6 [poly8]]"},
{"WITHIN", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, { {"WITHIN", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, {
"[7 [point9]]"}, "[7 [point9]]"},
{"WITHIN", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "SECTOR", "37.72999", "-122.44760", "1000", "0", "90"}, {
"[7 [point9]]"},
}) })
} }
@ -273,6 +279,8 @@ func keys_INTERSECTS_test(mc *mockServer) error {
] ]
] ]
}`}, {"[0 [point1 point2 line3 poly4 multipoly5 poly8]]"}, }`}, {"[0 [point1 point2 line3 poly4 multipoly5 poly8]]"},
{"INTERSECTS", "mykey", "IDS", "SECTOR", "37.72999", "-122.44760", "1000", "0", "90"}, {"[0 [point1 point2 line3 poly4 multipoly5 poly8]]"},
{"INTERSECTS", "mykey", "IDS", "SECTOR", "37.72999", "-122.44760", "1000", "0", "0"}, {"ERR equal bearings (0 == 0), use CIRCLE instead"},
{"SET", "key2", "poly9", "OBJECT", `{"type": "Polygon","coordinates": [[[-122.44037926197052,37.73313523548048],[-122.44017541408539,37.73313523548048],[-122.44017541408539,37.73336857568778],[-122.44037926197052,37.73336857568778],[-122.44037926197052,37.73313523548048]]]}`}, {"OK"}, {"SET", "key2", "poly9", "OBJECT", `{"type": "Polygon","coordinates": [[[-122.44037926197052,37.73313523548048],[-122.44017541408539,37.73313523548048],[-122.44017541408539,37.73336857568778],[-122.44037926197052,37.73336857568778],[-122.44037926197052,37.73313523548048]]]}`}, {"OK"},
{"SET", "key2", "poly10", "OBJECT", `{"type": "Polygon","coordinates": [[[-122.44040071964262,37.73359343010089],[-122.4402666091919,37.73359343010089],[-122.4402666091919,37.73373767596864],[-122.44040071964262,37.73373767596864],[-122.44040071964262,37.73359343010089]]]}`}, {"OK"}, {"SET", "key2", "poly10", "OBJECT", `{"type": "Polygon","coordinates": [[[-122.44040071964262,37.73359343010089],[-122.4402666091919,37.73359343010089],[-122.4402666091919,37.73373767596864],[-122.44040071964262,37.73373767596864],[-122.44040071964262,37.73359343010089]]]}`}, {"OK"},
@ -379,6 +387,8 @@ func keys_INTERSECTS_CURSOR_test(mc *mockServer) error {
"[6 [poly8]]"}, "[6 [poly8]]"},
{"INTERSECTS", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, { {"INTERSECTS", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, {
"[7 [point9]]"}, "[7 [point9]]"},
{"INTERSECTS", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "SECTOR", "37.72999", "-122.44760", "1000", "0", "90"}, {
"[7 [point9]]"},
}) })
} }
@ -398,6 +408,22 @@ func keys_WITHIN_CIRCLE_test(mc *mockServer) error {
}) })
} }
func keys_WITHIN_SECTOR_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "1", "POINT", 37.7324, -122.4424}, {"OK"},
{"SET", "mykey", "2", "POINT", 37.73241, -122.44241}, {"OK"},
{"SET", "mykey", "3", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
{"SET", "mykey", "4", "OBJECT", `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]]}`}, {"OK"},
{"SET", "mykey", "5", "OBJECT", `{"type":"MultiPolygon","coordinates":[[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]]]}`}, {"OK"},
{"SET", "mykey", "6", "POINT", -5, 5}, {"OK"},
{"SET", "mykey", "7", "POINT", 33, 21}, {"OK"},
{"WITHIN", "mykey", "IDS", "SECTOR", 37.731930, -122.443270, 1000, 0, 90}, {
"[0 [1 2 3 4 5]]"},
{"WITHIN", "mykey", "IDS", "SECTOR", 37.731930, -122.443270, 100, 0, 90}, {
"[0 [1 2]]"},
})
}
func keys_NEARBY_SPARSE_test(mc *mockServer) error { func keys_NEARBY_SPARSE_test(mc *mockServer) error {
// https://github.com/tidwall/tile38/issues/618 // https://github.com/tidwall/tile38/issues/618
return mc.DoBatch([][]interface{}{ return mc.DoBatch([][]interface{}{
@ -497,6 +523,22 @@ func keys_INTERSECTS_CIRCLE_test(mc *mockServer) error {
}) })
} }
func keys_INTERSECTS_SECTOR_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "1", "POINT", 37.7324, -122.4424}, {"OK"},
{"SET", "mykey", "2", "POINT", 37.73241, -122.44241}, {"OK"},
{"SET", "mykey", "3", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
{"SET", "mykey", "4", "OBJECT", `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]]}`}, {"OK"},
{"SET", "mykey", "5", "OBJECT", `{"type":"MultiPolygon","coordinates":[[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]]]}`}, {"OK"},
{"SET", "mykey", "6", "POINT", -5, 5}, {"OK"},
{"SET", "mykey", "7", "POINT", 33, 21}, {"OK"},
{"INTERSECTS", "mykey", "IDS", "SECTOR", 37.731930, -122.443270, 1000, 0, 90}, {
"[0 [1 2 3 4 5]]"},
{"INTERSECTS", "mykey", "IDS", "SECTOR", 37.731930, -122.443270, 100, 0, 90}, {
"[0 [1 2]]"},
})
}
func keys_SCAN_CURSOR_test(mc *mockServer) error { func keys_SCAN_CURSOR_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{ return mc.DoBatch([][]interface{}{
{"SET", "mykey", "id1", "FIELD", "foo", 1, "STRING", "bar1"}, {"OK"}, {"SET", "mykey", "id1", "FIELD", "foo", 1, "STRING", "bar1"}, {"OK"},

View File

@ -40,10 +40,12 @@ func testcmd_WITHIN_test(mc *mockServer) error {
{"SET", "mykey", "poly8", "OBJECT", poly8}, {"OK"}, {"SET", "mykey", "poly8", "OBJECT", poly8}, {"OK"},
{"TEST", "GET", "mykey", "point1", "WITHIN", "OBJECT", poly}, {"1"}, {"TEST", "GET", "mykey", "point1", "WITHIN", "OBJECT", poly}, {"1"},
{"TEST", "GET", "mykey", "point1", "WITHIN", "SECTOR", "37.72999", "-122.44760", "1000", "0", "90"}, {"1"},
{"TEST", "GET", "mykey", "line3", "WITHIN", "OBJECT", poly}, {"1"}, {"TEST", "GET", "mykey", "line3", "WITHIN", "OBJECT", poly}, {"1"},
{"TEST", "GET", "mykey", "poly4", "WITHIN", "OBJECT", poly}, {"1"}, {"TEST", "GET", "mykey", "poly4", "WITHIN", "OBJECT", poly}, {"1"},
{"TEST", "GET", "mykey", "multipoly5", "WITHIN", "OBJECT", poly}, {"1"}, {"TEST", "GET", "mykey", "multipoly5", "WITHIN", "OBJECT", poly}, {"1"},
{"TEST", "GET", "mykey", "poly8", "WITHIN", "OBJECT", poly}, {"1"}, {"TEST", "GET", "mykey", "poly8", "WITHIN", "OBJECT", poly}, {"1"},
{"TEST", "GET", "mykey", "poly8", "WITHIN", "SECTOR", "37.72999", "-122.44760", "1000", "0", "90"}, {"1"},
{"TEST", "GET", "mykey", "point6", "WITHIN", "OBJECT", poly}, {"0"}, {"TEST", "GET", "mykey", "point6", "WITHIN", "OBJECT", poly}, {"0"},
{"TEST", "GET", "mykey", "point7", "WITHIN", "OBJECT", poly}, {"0"}, {"TEST", "GET", "mykey", "point7", "WITHIN", "OBJECT", poly}, {"0"},

19
vendor/github.com/iwpnd/sectr/.gitignore generated vendored Normal file
View File

@ -0,0 +1,19 @@
.ipynb_checkpoints/
/tmp/
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
.DS_Store
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/

119
vendor/github.com/iwpnd/sectr/.gitlint generated vendored Normal file
View File

@ -0,0 +1,119 @@
# Edit this file as you like.
#
# All these sections are optional. Each section with the exception of [general] represents
# one rule and each key in it is an option for that specific rule.
#
# Rules and sections can be referenced by their full name or by id. For example
# section "[body-max-line-length]" could also be written as "[B1]". Full section names are
# used in here for clarity.
#
[general]
# Ignore certain rules, this example uses both full name and id
# ignore=title-trailing-punctuation, T3
# verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this
# verbosity = 2
# By default gitlint will ignore merge, revert, fixup and squash commits.
# ignore-merge-commits=true
# ignore-revert-commits=true
# ignore-fixup-commits=true
# ignore-squash-commits=true
# Ignore any data send to gitlint via stdin
# ignore-stdin=true
# Fetch additional meta-data from the local repository when manually passing a
# commit message to gitlint via stdin or --commit-msg. Disabled by default.
# staged=true
# Enable debug mode (prints more output). Disabled by default.
# debug=true
# Enable community contributed rules
# See http://jorisroovers.github.io/gitlint/contrib_rules for details
contrib=contrib-title-conventional-commits
# Set the extra-path where gitlint will search for user defined rules
# See http://jorisroovers.github.io/gitlint/user_defined_rules for details
# extra-path=examples/
# This is an example of how to configure the "title-max-length" rule and
# set the line-length it enforces to 80
# [title-max-length]
# line-length=50
# Conversely, you can also enforce minimal length of a title with the
# "title-min-length" rule:
# [title-min-length]
# min-length=5
# [title-must-not-contain-word]
# Comma-separated list of words that should not occur in the title. Matching is case
# insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING"
# will not cause a violation, but "WIP: my title" will.
# words=wip
# [title-match-regex]
# python-style regex that the commit-msg title must match
# Note that the regex can contradict with other rules if not used correctly
# (e.g. title-must-not-contain-word).
# regex=^US[0-9]*
# [body-max-line-length]
# line-length=72
# [body-min-length]
# min-length=5
#[body-is-missing]
# Whether to ignore this rule on merge commits (which typically only have a title)
# default = True
#ignore-merge-commits=false
# [body-changed-file-mention]
# List of files that need to be explicitly mentioned in the body when they are changed
# This is useful for when developers often erroneously edit certain files or git submodules.
# By specifying this rule, developers can only change the file when they explicitly reference
# it in the commit message.
# files=gitlint/rules.py,README.md
# [body-match-regex]
# python-style regex that the commit-msg body must match.
# E.g. body must end in My-Commit-Tag: foo
# regex=My-Commit-Tag: foo$
# [author-valid-email]
# python-style regex that the commit author email address must match.
# For example, use the following regex if you only want to allow email addresses from foo.com
# regex=[^@]+@foo.com
# [ignore-by-title]
# Ignore certain rules for commits of which the title matches a regex
# E.g. Match commit titles that start with "Release"
# regex=^Release(.*)
# Ignore certain rules, you can reference them by their id or by their full name
# Use 'all' to ignore all rules
ignore=B6,CC1
# [ignore-by-body]
# Ignore certain rules for commits of which the body has a line that matches a regex
# E.g. Match bodies that have a line that that contain "release"
# regex=(.*)release(.*)
#
# Ignore certain rules, you can reference them by their id or by their full name
# Use 'all' to ignore all rules
# ignore=T1,body-min-length
# [ignore-body-lines]
# Ignore certain lines in a commit body that match a regex.
# E.g. Ignore all lines that start with 'Co-Authored-By'
# regex=^Co-Authored-By
# This is a contrib rule - a community contributed rule. These are disabled by default.
# You need to explicitly enable them one-by-one by adding them to the "contrib" option
# under [general] section above.
# [contrib-title-conventional-commits]
# Specify allowed commit types. For details see: https://www.conventionalcommits.org/
#types = feat,fix,refactor,docs,ci,chore

13
vendor/github.com/iwpnd/sectr/.pre-commit-config.yaml generated vendored Normal file
View File

@ -0,0 +1,13 @@
repos:
- repo: git://github.com/dnephin/pre-commit-golang
rev: v0.4.0
hooks:
- id: go-fmt
- id: go-vet
- id: go-lint
- id: go-imports
- id: go-mod-tidy
- repo: https://github.com/jorisroovers/gitlint
rev: v0.15.1
hooks:
- id: gitlint

3
vendor/github.com/iwpnd/sectr/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/iwpnd/sectr
go 1.16

71
vendor/github.com/iwpnd/sectr/readme.md generated vendored Normal file
View File

@ -0,0 +1,71 @@
# sectr 🍕
Build a circular sector (pizza piece 😅 ) spanning the angle between two given bearings, a radius and a center point.
## installation
```
go get -u github.com/iwpnd/sectr
```
## usage
<img src=".github/img/sectr_bg.png" alt="Logo" width="500" height="227">
```go
package main
import (
"fmt"
"github.com/iwpnd/sectr"
)
func main() {
p := sectr.Point{Lat: 52.25, Lng: 13.37}
sector := sectr.NewSector(p, 100, 0, 90)
fmt.Printf("%s", sector.JSON())
}
>> {
"type": "Polygon",
"coordinates": [
[
[13.37,52.25],
[13.37,52.25089932],
[13.37012803,52.2508959],
[13.3702803,52.2508828],
[13.37040491,52.25086448],
[13.37055029,52.25083383],
[13.37068965,52.25079405],
[13.37080006,52.25075423],
[13.37092446,52.2506989],
[13.37103872,52.25063591],
[13.3711253,52.25057807],
[13.37121783,52.25050289],
[13.37128479,52.25043599],
[13.37135219,52.25035138],
[13.37140478,52.25026293],
[13.37143686,52.25018697],
[13.37146091,52.250094],
[13.37146896,52.24999999],
[13.37,52.25]
]
]
}
```
## License
MIT
## Acknowledgement
[Chris Veness](https://github.com/chrisveness) for refreshing my university left-overs with this blog [moveable-type](https://www.movable-type.co.uk/scripts/latlong.html)
## Maintainer
Benjamin Ramser - [@iwpnd](https://github.com/iwpnd)
Project Link: [https://github.com/iwpnd/sectr](https://github.com/iwpnd/sectr)

160
vendor/github.com/iwpnd/sectr/sectr.go generated vendored Normal file
View File

@ -0,0 +1,160 @@
package sectr
import (
"encoding/json"
"math"
)
const earthRadius = 6371008.8 // earth radius
// Point ...
type Point struct {
Lng, Lat float64
}
// Sector ...
type Sector struct {
coordinates [][][]float64
origin Point
radius float64
bearing1 float64
bearing2 float64
}
// SectorGeometry ...
type SectorGeometry struct {
Type string `json:"type"`
Coordinates [][][]float64 `json:"coordinates"`
}
func radToDegree(rad float64) float64 {
return rad * 180 / math.Pi
}
func degreeToRad(degree float64) float64 {
return degree * math.Pi / 180
}
func distanceToRadians(distance float64) float64 {
const r = earthRadius
return distance / r
}
// terminal calculates the terminal position travelling a distance
// from a given origin
// see https://www.movable-type.co.uk/scripts/latlong.html
func terminal(start Point, distance, bearing float64) Point {
φ1 := degreeToRad(start.Lat)
λ1 := degreeToRad(start.Lng)
bearingRad := degreeToRad(bearing)
distanceRad := distanceToRadians(distance)
φ2 := math.Asin(
math.Sin(φ1)*
math.Cos(distanceRad) +
math.Cos(φ1)*
math.Sin(distanceRad)*
math.Cos(bearingRad))
λ2 := λ1 + math.Atan2(
math.Sin(bearingRad)*
math.Sin(distanceRad)*
math.Cos(φ1),
math.Cos(distanceRad)-
math.Sin(φ1)*
math.Sin(φ2))
// cap decimals at .00000001 degree ~= 1.11mm
lng := math.Round(radToDegree(λ2)*100000000) / 100000000
lat := math.Round(radToDegree(φ2)*100000000) / 100000000
return Point{Lng: lng, Lat: lat}
}
func bearingToAngle(bearing float64) float64 {
angle := math.Mod(bearing, 360)
if angle < 0 {
angle = angle + 360
}
return angle
}
// NewSector creates a sector from a given origin point, a radius and two bearings
func NewSector(origin Point, radius, bearing1, bearing2 float64) *Sector {
// to cap the maximum positions in a sector/circle to 64
// the higher the smoother, yet the bigger the coordinate array
const steps = 64
s := &Sector{
origin: origin,
bearing1: bearing1,
bearing2: bearing2,
radius: radius,
}
angle1 := bearingToAngle(bearing1)
angle2 := bearingToAngle(bearing2)
// if angle1 == angle2 return circle
if angle1 == angle2 {
for i := 1; i < steps; i++ {
α := float64(i * -360 / steps)
t := terminal(origin, radius, α)
s.addPoint(t)
}
s.coordinates[0] = append(s.coordinates[0], s.coordinates[0][0])
return s
}
var endDegree float64
startDegree := angle1
if angle1 < angle2 {
endDegree = angle2
} else {
endDegree = angle2 + 360
}
α := startDegree
s.addPoint(origin)
for i := 1; ; i++ {
if α < endDegree {
t := terminal(origin, radius, α)
s.addPoint(t)
α = startDegree + float64((i*360)/steps)
}
if α >= endDegree {
t := terminal(origin, radius, endDegree)
s.addPoint(t)
s.addPoint(origin)
return s
}
}
}
func (s *Sector) addPoint(p Point) {
if len(s.coordinates) == 0 {
s.coordinates = append(s.coordinates, [][]float64{{p.Lng, p.Lat}})
return
}
s.coordinates[0] = append(s.coordinates[0], []float64{p.Lng, p.Lat})
}
// JSON exports the Sector as json
func (s Sector) JSON() []byte {
f := SectorGeometry{Type: "Polygon", Coordinates: s.coordinates}
j, _ := json.Marshal(f)
return j
}

3
vendor/modules.txt vendored
View File

@ -103,6 +103,9 @@ github.com/googleapis/gax-go/v2
github.com/gorilla/websocket github.com/gorilla/websocket
# github.com/hashicorp/go-uuid v1.0.2 # github.com/hashicorp/go-uuid v1.0.2
github.com/hashicorp/go-uuid github.com/hashicorp/go-uuid
# github.com/iwpnd/sectr v0.1.2
## explicit
github.com/iwpnd/sectr
# github.com/jcmturner/gofork v1.0.0 # github.com/jcmturner/gofork v1.0.0
github.com/jcmturner/gofork/encoding/asn1 github.com/jcmturner/gofork/encoding/asn1
github.com/jcmturner/gofork/x/crypto/pbkdf2 github.com/jcmturner/gofork/x/crypto/pbkdf2