mirror of https://github.com/tidwall/tile38.git
Merge branch 'iwpnd-feat/add-sector-area'
This commit is contained in:
commit
ad6e5aeab8
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
1
go.mod
|
@ -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
2
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
|
@ -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/
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/iwpnd/sectr
|
||||||
|
|
||||||
|
go 1.16
|
|
@ -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)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue