mirror of https://github.com/tidwall/tile38.git
Merge branch 'master' into field-optz
This commit is contained in:
commit
1a977d1010
|
@ -242,8 +242,7 @@
|
|||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:0a6da4a08925415ab3326e140985ec628417903637461e9b68c07a03dfcc918d"
|
||||
digest = "1:fc81262a6ad5aeec27e1bd15356f790e6b2d8fd14acb6bd5ff3f0f51bf67417f"
|
||||
name = "github.com/tidwall/geojson"
|
||||
packages = [
|
||||
".",
|
||||
|
@ -251,7 +250,8 @@
|
|||
"geometry",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "d0a98d02b48e887b4de454d80c61ef5fb9312482"
|
||||
revision = "6baab67ab6a9bac4abf153ab779c736254a37fd1"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3ddca2bd5496c6922a2a9e636530e178a43c2a534ea6634211acdc7d10222794"
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
required = [
|
||||
"github.com/tidwall/lotsa",
|
||||
"github.com/mmcloughlin/geohash",
|
||||
"github.com/tidwall/geojson",
|
||||
"github.com/tidwall/evio"
|
||||
]
|
||||
|
||||
|
@ -35,6 +34,10 @@ required = [
|
|||
branch = "master"
|
||||
name = "github.com/tidwall/boxtree"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/tidwall/geojson"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/Shopify/sarama"
|
||||
version = "1.13.0"
|
||||
|
|
|
@ -1785,5 +1785,256 @@
|
|||
"complexity": "O(1)",
|
||||
"since": "1.10.0",
|
||||
"group": "scripting"
|
||||
},
|
||||
"TEST":{
|
||||
"summary": "Performs spatial test",
|
||||
"complexity": "One test per command, complexity depends on the test",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "area1",
|
||||
"enumargs": [
|
||||
{
|
||||
"name": "POINT",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GET",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BOUNDS",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "minlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "minlon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OBJECT",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "geojson",
|
||||
"type": "geojson"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CIRCLE",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "meters",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TILE",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "x",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "QUADKEY",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "quadkey",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HASH",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "geohash",
|
||||
"type": "geohash"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "test",
|
||||
"enumargs": [
|
||||
{
|
||||
"name": "INTERSECTS"
|
||||
},
|
||||
{
|
||||
"name": "WITHIN"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": "CLIP",
|
||||
"name": [],
|
||||
"type": [],
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "area2",
|
||||
"enumargs": [
|
||||
{
|
||||
"name": "POINT",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GET",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BOUNDS",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "minlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "minlon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OBJECT",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "geojson",
|
||||
"type": "geojson"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CIRCLE",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "meters",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TILE",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "x",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "QUADKEY",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "quadkey",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HASH",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "geohash",
|
||||
"type": "geohash"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"since": "1.16.0",
|
||||
"group": "tests"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1951,5 +1951,256 @@ var commandsJSON = `{
|
|||
"complexity": "O(1)",
|
||||
"since": "1.10.0",
|
||||
"group": "scripting"
|
||||
},
|
||||
"TEST":{
|
||||
"summary": "Performs spatial test",
|
||||
"complexity": "One test per command, complexity depends on the test",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "area1",
|
||||
"enumargs": [
|
||||
{
|
||||
"name": "POINT",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GET",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BOUNDS",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "minlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "minlon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OBJECT",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "geojson",
|
||||
"type": "geojson"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CIRCLE",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "meters",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TILE",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "x",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "QUADKEY",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "quadkey",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HASH",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "geohash",
|
||||
"type": "geohash"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "test",
|
||||
"enumargs": [
|
||||
{
|
||||
"name": "INTERSECTS"
|
||||
},
|
||||
{
|
||||
"name": "WITHIN"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": "CLIP",
|
||||
"name": [],
|
||||
"type": [],
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"name": "area2",
|
||||
"enumargs": [
|
||||
{
|
||||
"name": "POINT",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GET",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BOUNDS",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "minlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "minlon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "maxlon",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OBJECT",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "geojson",
|
||||
"type": "geojson"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CIRCLE",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "lat",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "lon",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "meters",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TILE",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "x",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"type": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "QUADKEY",
|
||||
"arguments":[
|
||||
{
|
||||
"name": "quadkey",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "HASH",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "geohash",
|
||||
"type": "geohash"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"since": "1.16.0",
|
||||
"group": "tests"
|
||||
}
|
||||
}`
|
||||
|
|
|
@ -35,5 +35,5 @@ func clipPolygon(
|
|||
if newPoly.Empty() {
|
||||
return geojson.NewMultiPolygon(nil)
|
||||
}
|
||||
return polygon
|
||||
return newPoly
|
||||
}
|
||||
|
|
|
@ -58,6 +58,11 @@ func (s String) JSON() string {
|
|||
return string(s.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (s String) MarshalJSON() ([]byte, error) {
|
||||
return s.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// Within ...
|
||||
func (s String) Within(obj geojson.Object) bool {
|
||||
return false
|
||||
|
|
|
@ -404,10 +404,15 @@ func parseEndpoint(s string) (Endpoint, error) {
|
|||
// Parsing MQTT queue name
|
||||
if len(sp) > 1 {
|
||||
var err error
|
||||
endpoint.MQTT.QueueName, err = url.QueryUnescape(sp[1])
|
||||
if err != nil {
|
||||
return endpoint, errors.New("invalid MQTT topic name")
|
||||
var parts []string
|
||||
for _, part := range sp[1:] {
|
||||
part, err = url.QueryUnescape(part)
|
||||
if err != nil {
|
||||
return endpoint, errors.New("invalid MQTT topic name")
|
||||
}
|
||||
parts = append(parts, part)
|
||||
}
|
||||
endpoint.MQTT.QueueName = strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
// Parsing additional params
|
||||
|
|
|
@ -620,6 +620,8 @@ func (c *Server) commandInScript(msg *Message) (
|
|||
res, err = c.cmdType(msg)
|
||||
case "keys":
|
||||
res, err = c.cmdKeys(msg)
|
||||
case "test":
|
||||
res, err = c.cmdTest(msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -668,7 +670,7 @@ func (c *Server) luaTile38AtomicRW(msg *Message) (resp.Value, error) {
|
|||
return resp.NullValue(), errReadOnly
|
||||
}
|
||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
||||
"ttl", "bounds", "server", "info", "type", "jget":
|
||||
"ttl", "bounds", "server", "info", "type", "jget", "test":
|
||||
// read operations
|
||||
if c.config.followHost() != "" && !c.fcuponce {
|
||||
return resp.NullValue(), errCatchingUp
|
||||
|
@ -700,7 +702,7 @@ func (c *Server) luaTile38AtomicRO(msg *Message) (resp.Value, error) {
|
|||
return resp.NullValue(), errReadOnly
|
||||
|
||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
||||
"ttl", "bounds", "server", "info", "type", "jget":
|
||||
"ttl", "bounds", "server", "info", "type", "jget", "test":
|
||||
// read operations
|
||||
if c.config.followHost() != "" && !c.fcuponce {
|
||||
return resp.NullValue(), errCatchingUp
|
||||
|
@ -735,7 +737,7 @@ func (c *Server) luaTile38NonAtomic(msg *Message) (resp.Value, error) {
|
|||
return resp.NullValue(), errReadOnly
|
||||
}
|
||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
||||
"ttl", "bounds", "server", "info", "type", "jget":
|
||||
"ttl", "bounds", "server", "info", "type", "jget", "test":
|
||||
// read operations
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
|
|
@ -106,7 +106,7 @@ func (server *Server) cmdSearchArgs(
|
|||
fallthrough
|
||||
case "circle":
|
||||
if s.clip {
|
||||
err = errInvalidArgument("cannnot clip with " + ltyp)
|
||||
err = errInvalidArgument("cannot clip with " + ltyp)
|
||||
return
|
||||
}
|
||||
var slat, slon, smeters string
|
||||
|
@ -158,7 +158,7 @@ func (server *Server) cmdSearchArgs(
|
|||
s.obj = geojson.NewCircle(geometry.Point{X: lon, Y: lat}, meters, defaultCircleSteps)
|
||||
case "object":
|
||||
if s.clip {
|
||||
err = errInvalidArgument("cannnot clip with object")
|
||||
err = errInvalidArgument("cannot clip with object")
|
||||
return
|
||||
}
|
||||
var obj string
|
||||
|
@ -272,7 +272,7 @@ func (server *Server) cmdSearchArgs(
|
|||
})
|
||||
case "get":
|
||||
if s.clip {
|
||||
err = errInvalidArgument("cannnot clip with get")
|
||||
err = errInvalidArgument("cannot clip with get")
|
||||
}
|
||||
var key, id string
|
||||
if vs, key, ok = tokenval(vs); !ok || key == "" {
|
||||
|
|
|
@ -1203,6 +1203,8 @@ func (server *Server) command(msg *Message, client *Client) (
|
|||
res, err = server.cmdPsubscribe(msg)
|
||||
case "publish":
|
||||
res, err = server.cmdPublish(msg)
|
||||
case "test":
|
||||
res, err = server.cmdTest(msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
package server
|
||||
|
||||
// TEST command: spatial tests without walking the tree.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mmcloughlin/geohash"
|
||||
"github.com/tidwall/geojson"
|
||||
"github.com/tidwall/geojson/geometry"
|
||||
"github.com/tidwall/resp"
|
||||
"github.com/tidwall/tile38/internal/bing"
|
||||
"github.com/tidwall/tile38/internal/clip"
|
||||
)
|
||||
|
||||
func (s *Server) parseArea(ovs []string, doClip bool) (vs []string, o geojson.Object, err error) {
|
||||
var ok bool
|
||||
var typ string
|
||||
vs = ovs[:]
|
||||
if vs, typ, ok = tokenval(vs); !ok || typ == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
ltyp := strings.ToLower(typ)
|
||||
switch ltyp {
|
||||
case "point":
|
||||
var slat, slon string
|
||||
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, slon, ok = tokenval(vs); !ok || slon == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
var lat, lon 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
|
||||
}
|
||||
o = geojson.NewPoint(geometry.Point{X: lon, Y: lat})
|
||||
case "circle":
|
||||
if doClip {
|
||||
err = fmt.Errorf("invalid clip type '%s'", typ)
|
||||
return
|
||||
}
|
||||
var slat, slon, smeters string
|
||||
if vs, slat, ok = tokenval(vs); !ok || slat == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, slon, ok = tokenval(vs); !ok || slon == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
var lat, lon, meters 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 vs, smeters, ok = tokenval(vs); !ok || smeters == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if meters, err = strconv.ParseFloat(smeters, 64); err != nil {
|
||||
err = errInvalidArgument(smeters)
|
||||
return
|
||||
}
|
||||
if meters < 0 {
|
||||
err = errInvalidArgument(smeters)
|
||||
return
|
||||
}
|
||||
o = geojson.NewCircle(geometry.Point{X: lon, Y: lat}, meters, defaultCircleSteps)
|
||||
case "object":
|
||||
if doClip {
|
||||
err = fmt.Errorf("invalid clip type '%s'", typ)
|
||||
return
|
||||
}
|
||||
var obj string
|
||||
if vs, obj, ok = tokenval(vs); !ok || obj == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
o, err = geojson.Parse(obj, &s.geomParseOpts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "bounds":
|
||||
var sminLat, sminLon, smaxlat, smaxlon string
|
||||
if vs, sminLat, ok = tokenval(vs); !ok || sminLat == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, sminLon, ok = tokenval(vs); !ok || sminLon == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, smaxlat, ok = tokenval(vs); !ok || smaxlat == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, smaxlon, ok = tokenval(vs); !ok || smaxlon == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
var minLat, minLon, maxLat, maxLon float64
|
||||
if minLat, err = strconv.ParseFloat(sminLat, 64); err != nil {
|
||||
err = errInvalidArgument(sminLat)
|
||||
return
|
||||
}
|
||||
if minLon, err = strconv.ParseFloat(sminLon, 64); err != nil {
|
||||
err = errInvalidArgument(sminLon)
|
||||
return
|
||||
}
|
||||
if maxLat, err = strconv.ParseFloat(smaxlat, 64); err != nil {
|
||||
err = errInvalidArgument(smaxlat)
|
||||
return
|
||||
}
|
||||
if maxLon, err = strconv.ParseFloat(smaxlon, 64); err != nil {
|
||||
err = errInvalidArgument(smaxlon)
|
||||
return
|
||||
}
|
||||
o = geojson.NewRect(geometry.Rect{
|
||||
Min: geometry.Point{X: minLon, Y: minLat},
|
||||
Max: geometry.Point{X: maxLon, Y: maxLat},
|
||||
})
|
||||
case "hash":
|
||||
var hash string
|
||||
if vs, hash, ok = tokenval(vs); !ok || hash == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
box := geohash.BoundingBox(hash)
|
||||
o = geojson.NewRect(geometry.Rect{
|
||||
Min: geometry.Point{X: box.MinLng, Y: box.MinLat},
|
||||
Max: geometry.Point{X: box.MaxLng, Y: box.MaxLat},
|
||||
})
|
||||
case "quadkey":
|
||||
var key string
|
||||
if vs, key, ok = tokenval(vs); !ok || key == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
var minLat, minLon, maxLat, maxLon float64
|
||||
minLat, minLon, maxLat, maxLon, err = bing.QuadKeyToBounds(key)
|
||||
if err != nil {
|
||||
err = errInvalidArgument(key)
|
||||
return
|
||||
}
|
||||
o = geojson.NewRect(geometry.Rect{
|
||||
Min: geometry.Point{X: minLon, Y: minLat},
|
||||
Max: geometry.Point{X: maxLon, Y: maxLat},
|
||||
})
|
||||
case "tile":
|
||||
var sx, sy, sz string
|
||||
if vs, sx, ok = tokenval(vs); !ok || sx == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, sy, ok = tokenval(vs); !ok || sy == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, sz, ok = tokenval(vs); !ok || sz == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
var x, y int64
|
||||
var z uint64
|
||||
if x, err = strconv.ParseInt(sx, 10, 64); err != nil {
|
||||
err = errInvalidArgument(sx)
|
||||
return
|
||||
}
|
||||
if y, err = strconv.ParseInt(sy, 10, 64); err != nil {
|
||||
err = errInvalidArgument(sy)
|
||||
return
|
||||
}
|
||||
if z, err = strconv.ParseUint(sz, 10, 64); err != nil {
|
||||
err = errInvalidArgument(sz)
|
||||
return
|
||||
}
|
||||
var minLat, minLon, maxLat, maxLon float64
|
||||
minLat, minLon, maxLat, maxLon = bing.TileXYToBounds(x, y, z)
|
||||
o = geojson.NewRect(geometry.Rect{
|
||||
Min: geometry.Point{X: minLon, Y: minLat},
|
||||
Max: geometry.Point{X: maxLon, Y: maxLat},
|
||||
})
|
||||
case "get":
|
||||
if doClip {
|
||||
err = fmt.Errorf("invalid clip type '%s'", typ)
|
||||
return
|
||||
}
|
||||
var key, id string
|
||||
if vs, key, ok = tokenval(vs); !ok || key == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
if vs, id, ok = tokenval(vs); !ok || id == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
col := s.getCol(key)
|
||||
if col == nil {
|
||||
err = errKeyNotFound
|
||||
return
|
||||
}
|
||||
o, _, ok = col.Get(id)
|
||||
if !ok {
|
||||
err = errIDNotFound
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Server) cmdTest(msg *Message) (res resp.Value, err error) {
|
||||
start := time.Now()
|
||||
vs := msg.Args[1:]
|
||||
|
||||
var ok bool
|
||||
var test string
|
||||
var obj1, obj2, clipped geojson.Object
|
||||
if vs, obj1, err = s.parseArea(vs, false); err != nil {
|
||||
return
|
||||
}
|
||||
if vs, test, ok = tokenval(vs); !ok || test == "" {
|
||||
err = errInvalidNumberOfArguments
|
||||
return
|
||||
}
|
||||
lTest := strings.ToLower(test)
|
||||
if lTest != "within" && lTest != "intersects" {
|
||||
err = errInvalidArgument(test)
|
||||
return
|
||||
}
|
||||
var wtok string
|
||||
var nvs []string
|
||||
var doClip bool
|
||||
nvs, wtok, ok = tokenval(vs)
|
||||
if ok && len(wtok) > 0 {
|
||||
switch strings.ToLower(wtok) {
|
||||
case "clip":
|
||||
vs = nvs
|
||||
if lTest != "intersects" {
|
||||
err = errInvalidArgument(wtok)
|
||||
return
|
||||
}
|
||||
doClip = true
|
||||
}
|
||||
}
|
||||
if vs, obj2, err = s.parseArea(vs, doClip); err != nil {
|
||||
return
|
||||
}
|
||||
if len(vs) != 0 {
|
||||
err = errInvalidNumberOfArguments
|
||||
}
|
||||
|
||||
var result int
|
||||
if lTest == "within" {
|
||||
if obj1.Within(obj2) {
|
||||
result = 1
|
||||
}
|
||||
} else if lTest == "intersects" {
|
||||
if obj1.Intersects(obj2) {
|
||||
result = 1
|
||||
if doClip {
|
||||
clipped = clip.Clip(obj1, obj2)
|
||||
}
|
||||
}
|
||||
}
|
||||
switch msg.OutputType {
|
||||
case JSON:
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(`{"ok":true`)
|
||||
if result != 0 {
|
||||
buf.WriteString(`,"result":true`)
|
||||
} else {
|
||||
buf.WriteString(`,"result":false`)
|
||||
}
|
||||
if clipped != nil {
|
||||
buf.WriteString(`,"object":` + clipped.JSON())
|
||||
}
|
||||
buf.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
|
||||
return resp.StringValue(buf.String()), nil
|
||||
case RESP:
|
||||
if clipped != nil {
|
||||
return resp.ArrayValue([]resp.Value{
|
||||
resp.IntegerValue(result),
|
||||
resp.StringValue(clipped.JSON())}), nil
|
||||
}
|
||||
return resp.IntegerValue(result), nil
|
||||
}
|
||||
return NOMessage, nil
|
||||
}
|
|
@ -30,7 +30,7 @@ func json_JSET_geojson_test(mc *mockServer) error {
|
|||
{"JSET", "mykey", "myid1", "coordinates.1", 44}, {"OK"},
|
||||
{"JGET", "mykey", "myid1"}, {`{"type":"Point","coordinates":[-115,44]}`},
|
||||
{"SET", "mykey", "myid1", "OBJECT", `{"type":"Feature","geometry":{"type":"Point","coordinates":[-115,44]}}`}, {"OK"},
|
||||
{"JGET", "mykey", "myid1"}, {`{"type":"Feature","geometry":{"type":"Point","coordinates":[-115,44]}}`},
|
||||
{"JGET", "mykey", "myid1"}, {`{"type":"Feature","geometry":{"type":"Point","coordinates":[-115,44]},"properties":{}}`},
|
||||
{"JGET", "mykey", "myid1", "geometry.type"}, {"Point"},
|
||||
{"JSET", "mykey", "myid1", "properties.tags.-1", "southwest"}, {"OK"},
|
||||
{"JSET", "mykey", "myid1", "properties.tags.-1", "united states"}, {"OK"},
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func subTestTestCmd(t *testing.T, mc *mockServer) {
|
||||
runStep(t, mc, "WITHIN", testcmd_WITHIN_test)
|
||||
runStep(t, mc, "INTERSECTS", testcmd_INTERSECTS_test)
|
||||
runStep(t, mc, "INTERSECTS_CLIP", testcmd_INTERSECTS_CLIP_test)
|
||||
}
|
||||
|
||||
func testcmd_WITHIN_test(mc *mockServer) error {
|
||||
poly := `{
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[-122.44126439094543,37.72906137107],
|
||||
[-122.43980526924135,37.72906137107],
|
||||
[-122.43980526924135,37.73421283683962],
|
||||
[-122.44126439094543,37.73421283683962],
|
||||
[-122.44126439094543,37.72906137107]
|
||||
]
|
||||
]
|
||||
}`
|
||||
poly8 := `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]],[[-122.44060993194579,37.73345766902749],[-122.44044363498686,37.73345766902749],[-122.44044363498686,37.73355524732416],[-122.44060993194579,37.73355524732416],[-122.44060993194579,37.73345766902749]],[[-122.44060724973677,37.7336888869566],[-122.4402102828026,37.7336888869566],[-122.4402102828026,37.7339752567853],[-122.44060724973677,37.7339752567853],[-122.44060724973677,37.7336888869566]]]}`
|
||||
poly9 := `{"type":"Polygon","coordinates":[[[-122.44037926197052,37.73313523548048],[-122.44017541408539,37.73313523548048],[-122.44017541408539,37.73336857568778],[-122.44037926197052,37.73336857568778],[-122.44037926197052,37.73313523548048]]]}`
|
||||
poly10 := `{"type":"Polygon","coordinates":[[[-122.44040071964262,37.73359343010089],[-122.4402666091919,37.73359343010089],[-122.4402666091919,37.73373767596864],[-122.44040071964262,37.73373767596864],[-122.44040071964262,37.73359343010089]]]}`
|
||||
|
||||
return mc.DoBatch([][]interface{}{
|
||||
{"SET", "mykey", "point1", "POINT", 37.7335, -122.4412}, {"OK"},
|
||||
{"SET", "mykey", "point2", "POINT", 37.7335, -122.44121}, {"OK"},
|
||||
{"SET", "mykey", "line3", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
|
||||
{"SET", "mykey", "poly4", "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", "multipoly5", "OBJECT", `{"type":"MultiPolygon","coordinates":[[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]],[[[-122.44091033935547,37.731981251280985],[-122.43994474411011,37.731981251280985],[-122.43994474411011,37.73254976045042],[-122.44091033935547,37.73254976045042],[-122.44091033935547,37.731981251280985]]]]}`}, {"OK"},
|
||||
{"SET", "mykey", "point6", "POINT", -5, 5}, {"OK"},
|
||||
{"SET", "mykey", "point7", "POINT", 33, 21}, {"OK"},
|
||||
{"SET", "mykey", "poly8", "OBJECT", poly8}, {"OK"},
|
||||
|
||||
{"TEST", "GET", "mykey", "point1", "WITHIN", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "line3", "WITHIN", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "poly4", "WITHIN", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "multipoly5", "WITHIN", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "poly8", "WITHIN", "OBJECT", poly}, {"1"},
|
||||
|
||||
{"TEST", "GET", "mykey", "point6", "WITHIN", "OBJECT", poly}, {"0"},
|
||||
{"TEST", "GET", "mykey", "point7", "WITHIN", "OBJECT", poly}, {"0"},
|
||||
|
||||
{"TEST", "OBJECT", poly9, "WITHIN", "OBJECT", poly8}, {"1"},
|
||||
{"TEST", "OBJECT", poly10, "WITHIN", "OBJECT", poly8}, {"0"},
|
||||
})
|
||||
}
|
||||
|
||||
func testcmd_INTERSECTS_test(mc *mockServer) error {
|
||||
poly := `{
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[-122.44126439094543,37.732906137107],
|
||||
[-122.43980526924135,37.732906137107],
|
||||
[-122.43980526924135,37.73421283683962],
|
||||
[-122.44126439094543,37.73421283683962],
|
||||
[-122.44126439094543,37.732906137107]
|
||||
]
|
||||
]
|
||||
}`
|
||||
poly8 := `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]],[[-122.44060993194579,37.73345766902749],[-122.44044363498686,37.73345766902749],[-122.44044363498686,37.73355524732416],[-122.44060993194579,37.73355524732416],[-122.44060993194579,37.73345766902749]],[[-122.44060724973677,37.7336888869566],[-122.4402102828026,37.7336888869566],[-122.4402102828026,37.7339752567853],[-122.44060724973677,37.7339752567853],[-122.44060724973677,37.7336888869566]]]}`
|
||||
poly9 := `{"type": "Polygon","coordinates": [[[-122.44037926197052,37.73313523548048],[-122.44017541408539,37.73313523548048],[-122.44017541408539,37.73336857568778],[-122.44037926197052,37.73336857568778],[-122.44037926197052,37.73313523548048]]]}`
|
||||
poly10 := `{"type": "Polygon","coordinates": [[[-122.44040071964262,37.73359343010089],[-122.4402666091919,37.73359343010089],[-122.4402666091919,37.73373767596864],[-122.44040071964262,37.73373767596864],[-122.44040071964262,37.73359343010089]]]}`
|
||||
poly101 := `{"type":"Polygon","coordinates":[[[-122.44051605463028,37.73375464605226],[-122.44028002023695,37.73375464605226],[-122.44028002023695,37.733903134117966],[-122.44051605463028,37.733903134117966],[-122.44051605463028,37.73375464605226]]]}`
|
||||
|
||||
return mc.DoBatch([][]interface{}{
|
||||
{"SET", "mykey", "point1", "POINT", 37.7335, -122.4412}, {"OK"},
|
||||
{"SET", "mykey", "point2", "POINT", 37.7335, -122.44121}, {"OK"},
|
||||
{"SET", "mykey", "line3", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
|
||||
{"SET", "mykey", "poly4", "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", "multipoly5", "OBJECT", `{"type":"MultiPolygon","coordinates":[[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]],[[[-122.44091033935547,37.731981251280985],[-122.43994474411011,37.731981251280985],[-122.43994474411011,37.73254976045042],[-122.44091033935547,37.73254976045042],[-122.44091033935547,37.731981251280985]]]]}`}, {"OK"},
|
||||
{"SET", "mykey", "point6", "POINT", -5, 5}, {"OK"},
|
||||
{"SET", "mykey", "point7", "POINT", 33, 21}, {"OK"},
|
||||
{"SET", "mykey", "poly8", "OBJECT", poly8}, {"OK"},
|
||||
|
||||
{"TEST", "GET", "mykey", "point1", "INTERSECTS", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "point2", "INTERSECTS", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "line3", "INTERSECTS", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "poly4", "INTERSECTS", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "multipoly5", "INTERSECTS", "OBJECT", poly}, {"1"},
|
||||
{"TEST", "GET", "mykey", "poly8", "INTERSECTS", "OBJECT", poly}, {"1"},
|
||||
|
||||
{"TEST", "GET", "mykey", "point6", "INTERSECTS", "OBJECT", poly}, {"0"},
|
||||
{"TEST", "GET", "mykey", "point7", "INTERSECTS", "OBJECT", poly}, {"0"},
|
||||
|
||||
{"TEST", "OBJECT", poly9, "INTERSECTS", "OBJECT", poly8}, {"1"},
|
||||
{"TEST", "OBJECT", poly10, "INTERSECTS", "OBJECT", poly8}, {"1"},
|
||||
{"TEST", "OBJECT", poly101, "INTERSECTS", "OBJECT", poly8}, {"0"},
|
||||
})
|
||||
}
|
||||
|
||||
func testcmd_INTERSECTS_CLIP_test(mc *mockServer) error {
|
||||
poly8 := `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]],[[-122.44060993194579,37.73345766902749],[-122.44044363498686,37.73345766902749],[-122.44044363498686,37.73355524732416],[-122.44060993194579,37.73355524732416],[-122.44060993194579,37.73345766902749]],[[-122.44060724973677,37.7336888869566],[-122.4402102828026,37.7336888869566],[-122.4402102828026,37.7339752567853],[-122.44060724973677,37.7339752567853],[-122.44060724973677,37.7336888869566]]]}`
|
||||
poly9 := `{"type":"Polygon","coordinates":[[[-122.44037926197052,37.73313523548048],[-122.44017541408539,37.73313523548048],[-122.44017541408539,37.73336857568778],[-122.44037926197052,37.73336857568778],[-122.44037926197052,37.73313523548048]]]}`
|
||||
multipoly5 := `{"type":"MultiPolygon","coordinates":[[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]],[[[-122.44091033935547,37.731981251280985],[-122.43994474411011,37.731981251280985],[-122.43994474411011,37.73254976045042],[-122.44091033935547,37.73254976045042],[-122.44091033935547,37.731981251280985]]]]}`
|
||||
poly101 := `{"type":"Polygon","coordinates":[[[-122.44051605463028,37.73375464605226],[-122.44028002023695,37.73375464605226],[-122.44028002023695,37.733903134117966],[-122.44051605463028,37.733903134117966],[-122.44051605463028,37.73375464605226]]]}`
|
||||
|
||||
return mc.DoBatch([][]interface{}{
|
||||
{"SET", "mykey", "point1", "POINT", 37.7335, -122.4412}, {"OK"},
|
||||
|
||||
{"TEST", "OBJECT", poly9, "INTERSECTS", "CLIP", "OBJECT", "{}"}, {"ERR invalid clip type 'OBJECT'"},
|
||||
{"TEST", "OBJECT", poly9, "INTERSECTS", "CLIP", "CIRCLE", "1", "2", "3"}, {"ERR invalid clip type 'CIRCLE'"},
|
||||
{"TEST", "OBJECT", poly9, "INTERSECTS", "CLIP", "GET", "mykey", "point1"}, {"ERR invalid clip type 'GET'"},
|
||||
{"TEST", "OBJECT", poly9, "WITHIN", "CLIP", "BOUNDS", 10, 10, 20, 20}, {"ERR invalid argument 'CLIP'"},
|
||||
|
||||
{"TEST", "OBJECT", poly9, "INTERSECTS", "CLIP", "BOUNDS", 37.732906137107, -122.44126439094543, 37.73421283683962, -122.43980526924135}, {"[1 " + poly9 + "]"},
|
||||
{"TEST", "OBJECT", poly8, "INTERSECTS", "CLIP", "BOUNDS", 37.733, -122.4408378, 37.7341129, -122.44}, {"[1 " + poly8 + "]"},
|
||||
{"TEST", "OBJECT", multipoly5, "INTERSECTS", "CLIP", "BOUNDS", 37.73227823422744, -122.44120001792908, 37.73319038868677, -122.43955314159392}, {"[1 " + `{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-122.4408378,37.73319038868677],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.73319038868677],[-122.4408378,37.73319038868677]]]},"properties":{}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-122.44091033935547,37.73227823422744],[-122.43994474411011,37.73227823422744],[-122.43994474411011,37.73254976045042],[-122.44091033935547,37.73254976045042],[-122.44091033935547,37.73227823422744]]]},"properties":{}}]}` + "]"},
|
||||
{"TEST", "OBJECT", poly101, "INTERSECTS", "CLIP", "BOUNDS", 37.73315644825698, -122.44054287672043, 37.73349585185455, -122.44008690118788}, {"0"},
|
||||
})
|
||||
}
|
|
@ -42,6 +42,7 @@ func TestAll(t *testing.T) {
|
|||
runSubTest(t, "keys", mc, subTestKeys)
|
||||
runSubTest(t, "json", mc, subTestJSON)
|
||||
runSubTest(t, "search", mc, subTestSearch)
|
||||
runSubTest(t, "testcmd", mc, subTestTestCmd)
|
||||
runSubTest(t, "fence", mc, subTestFence)
|
||||
runSubTest(t, "scripts", mc, subTestScripts)
|
||||
runSubTest(t, "info", mc, subTestInfo)
|
||||
|
@ -56,7 +57,9 @@ func runSubTest(t *testing.T, name string, mc *mockServer, test func(t *testing.
|
|||
}
|
||||
|
||||
func runStep(t *testing.T, mc *mockServer, name string, step func(mc *mockServer) error) {
|
||||
t.Helper()
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Helper()
|
||||
if err := func() error {
|
||||
// reset the current server
|
||||
mc.ResetConn()
|
||||
|
|
|
@ -53,6 +53,11 @@ func (g *Circle) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *Circle) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// String ...
|
||||
func (g *Circle) String() string {
|
||||
return string(g.AppendJSON(nil))
|
||||
|
|
|
@ -90,6 +90,11 @@ func (g *collection) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *collection) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// String ...
|
||||
func (g *collection) String() string {
|
||||
return string(g.AppendJSON(nil))
|
||||
|
|
|
@ -77,7 +77,7 @@ func (g *Feature) Members() string {
|
|||
func (g *Feature) AppendJSON(dst []byte) []byte {
|
||||
dst = append(dst, `{"type":"Feature","geometry":`...)
|
||||
dst = g.base.AppendJSON(dst)
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, true)
|
||||
dst = append(dst, '}')
|
||||
return dst
|
||||
|
||||
|
@ -93,6 +93,11 @@ func (g *Feature) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *Feature) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// Spatial ...
|
||||
func (g *Feature) Spatial() Spatial {
|
||||
return g
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
package geojson
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func TestFeatureParse(t *testing.T) {
|
||||
p := expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]}}`, nil)
|
||||
p := expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"properties":{}}`, nil)
|
||||
expect(t, p.Center() == P(1, 2))
|
||||
expectJSON(t, `{"type":"Feature"}`, errGeometryMissing)
|
||||
expectJSON(t, `{"type":"Feature","geometry":null}`, errDataInvalid)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":null}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"id":[4,true]}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":null,"properties":{}}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"id":[4,true],"properties":{}}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"id":"15","properties":{"a":"b"}}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":[1,2,3,4]}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2],"bbox":[1,2,3,4]},"id":[4,true]}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":[1,2,3,4],"properties":{}}`, nil)
|
||||
expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2],"bbox":[1,2,3,4]},"id":[4,true],"properties":{}}`, nil)
|
||||
}
|
||||
|
||||
func TestFeatureVarious(t *testing.T) {
|
||||
var g = expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]}}`, nil)
|
||||
expect(t, string(g.AppendJSON(nil)) == `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]}}`)
|
||||
var g = expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"properties":{}}`, nil)
|
||||
expect(t, string(g.AppendJSON(nil)) == `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"properties":{}}`)
|
||||
expect(t, g.Rect() == R(1, 2, 1, 2))
|
||||
expect(t, g.Center() == P(1, 2))
|
||||
expect(t, !g.Empty())
|
||||
|
||||
g = expectJSONOpts(t,
|
||||
`{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":[1,2,3,4]}`,
|
||||
`{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":[1,2,3,4],"properties":{}}`,
|
||||
nil, nil)
|
||||
expect(t, !g.Empty())
|
||||
expect(t, g.Rect() == R(1, 2, 1, 2))
|
||||
|
@ -30,10 +34,44 @@ func TestFeatureVarious(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
// func TestFeaturePoly(t *testing.T) {
|
||||
// p := expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]}}`, nil)
|
||||
// expect(t, p.Intersects(PO(1, 2)))
|
||||
// expect(t, p.Contains(PO(1, 2)))
|
||||
// expect(t, p.Within(PO(1, 2)))
|
||||
func TestFeatureProperties(t *testing.T) {
|
||||
obj, err := Parse(`{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]}}`, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
json := obj.JSON()
|
||||
if !gjson.Valid(json) {
|
||||
t.Fatal("invalid json")
|
||||
}
|
||||
if !gjson.Get(json, "properties").Exists() {
|
||||
t.Fatal("expected 'properties' member")
|
||||
}
|
||||
|
||||
// }
|
||||
obj, err = Parse(`{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"properties":true}`, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
json = obj.JSON()
|
||||
if !gjson.Valid(json) {
|
||||
t.Fatal("invalid json")
|
||||
}
|
||||
if gjson.Get(json, "properties").Type != gjson.True {
|
||||
t.Fatal("expected 'properties' member to be 'true'")
|
||||
}
|
||||
|
||||
obj, err = Parse(`{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"id":{}}`, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
json = obj.JSON()
|
||||
if !gjson.Valid(json) {
|
||||
t.Fatal("invalid json")
|
||||
}
|
||||
if !gjson.Get(json, "properties").Exists() {
|
||||
t.Fatal("expected 'properties' member")
|
||||
}
|
||||
if gjson.Get(json, "id").String() != "{}" {
|
||||
t.Fatal("expected 'id' member")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func (g *FeatureCollection) AppendJSON(dst []byte) []byte {
|
|||
}
|
||||
dst = append(dst, ']')
|
||||
if g.extra != nil {
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
strings.Index("", " ")
|
||||
|
@ -45,6 +45,11 @@ func (g *FeatureCollection) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *FeatureCollection) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
func parseJSONFeatureCollection(
|
||||
keys *parseKeys, opts *ParseOptions,
|
||||
) (Object, error) {
|
||||
|
|
|
@ -28,7 +28,7 @@ func (g *GeometryCollection) AppendJSON(dst []byte) []byte {
|
|||
}
|
||||
dst = append(dst, ']')
|
||||
if g.extra != nil {
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
strings.Index("", " ")
|
||||
|
@ -45,6 +45,11 @@ func (g *GeometryCollection) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *GeometryCollection) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
func parseJSONGeometryCollection(
|
||||
keys *parseKeys, opts *ParseOptions,
|
||||
) (Object, error) {
|
||||
|
|
|
@ -46,7 +46,7 @@ func (g *LineString) AppendJSON(dst []byte) []byte {
|
|||
dst = append(dst, `{"type":"LineString","coordinates":`...)
|
||||
dst, _ = appendJSONSeries(dst, &g.base, g.extra, 0)
|
||||
if g.extra != nil {
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
return dst
|
||||
|
@ -62,6 +62,11 @@ func (g *LineString) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *LineString) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// Spatial ...
|
||||
func (g *LineString) Spatial() Spatial {
|
||||
return g
|
||||
|
|
|
@ -30,7 +30,7 @@ func (g *MultiLineString) AppendJSON(dst []byte) []byte {
|
|||
}
|
||||
dst = append(dst, ']')
|
||||
if g.extra != nil {
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
return dst
|
||||
|
@ -58,6 +58,11 @@ func (g *MultiLineString) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *MultiLineString) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
func parseJSONMultiLineString(
|
||||
keys *parseKeys, opts *ParseOptions,
|
||||
) (Object, error) {
|
||||
|
|
|
@ -30,7 +30,7 @@ func (g *MultiPoint) AppendJSON(dst []byte) []byte {
|
|||
}
|
||||
dst = append(dst, ']')
|
||||
if g.extra != nil {
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
return dst
|
||||
|
@ -46,6 +46,11 @@ func (g *MultiPoint) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *MultiPoint) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
func parseJSONMultiPoint(keys *parseKeys, opts *ParseOptions) (Object, error) {
|
||||
var g MultiPoint
|
||||
var err error
|
||||
|
|
|
@ -30,7 +30,7 @@ func (g *MultiPolygon) AppendJSON(dst []byte) []byte {
|
|||
}
|
||||
dst = append(dst, ']')
|
||||
if g.extra != nil {
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
return dst
|
||||
|
@ -57,6 +57,11 @@ func (g *MultiPolygon) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *MultiPolygon) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
func parseJSONMultiPolygon(
|
||||
keys *parseKeys, opts *ParseOptions,
|
||||
) (Object, error) {
|
||||
|
|
|
@ -43,6 +43,7 @@ type Object interface {
|
|||
NumPoints() int
|
||||
ForEach(iter func(geom Object) bool) bool
|
||||
Spatial() Spatial
|
||||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
var _ = []Object{
|
||||
|
@ -88,7 +89,7 @@ type ParseOptions struct {
|
|||
// IndexGeometryKind is the kind of index implementation.
|
||||
// Default is QuadTreeCompressed
|
||||
IndexGeometryKind geometry.IndexKind
|
||||
RequireValid bool
|
||||
RequireValid bool
|
||||
}
|
||||
|
||||
// DefaultParseOptions ...
|
||||
|
@ -96,7 +97,7 @@ var DefaultParseOptions = &ParseOptions{
|
|||
IndexChildren: 64,
|
||||
IndexGeometry: 64,
|
||||
IndexGeometryKind: geometry.QuadTree,
|
||||
RequireValid: false,
|
||||
RequireValid: false,
|
||||
}
|
||||
|
||||
// Parse a GeoJSON object
|
||||
|
@ -244,10 +245,17 @@ func appendJSONPoint(dst []byte, point geometry.Point, ex *extra, idx int) []byt
|
|||
return dst
|
||||
}
|
||||
|
||||
func (ex *extra) appendJSONExtra(dst []byte) []byte {
|
||||
func (ex *extra) appendJSONExtra(dst []byte, propertiesRequired bool) []byte {
|
||||
if ex != nil && ex.members != "" {
|
||||
dst = append(dst, ',')
|
||||
dst = append(dst, ex.members[1:len(ex.members)-1]...)
|
||||
if propertiesRequired {
|
||||
if !gjson.Get(ex.members, "properties").Exists() {
|
||||
dst = append(dst, `,"properties":{}`...)
|
||||
}
|
||||
}
|
||||
} else if propertiesRequired {
|
||||
dst = append(dst, `,"properties":{}`...)
|
||||
}
|
||||
|
||||
return dst
|
||||
|
|
|
@ -101,9 +101,9 @@ func expect(t testing.TB, what bool) {
|
|||
}
|
||||
if !what {
|
||||
if t == nil {
|
||||
panic("exception failure")
|
||||
panic("expectation failure")
|
||||
} else {
|
||||
t.Fatal("expection failure")
|
||||
t.Fatal("expectation failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ func (g *Point) Base() geometry.Point {
|
|||
func (g *Point) AppendJSON(dst []byte) []byte {
|
||||
dst = append(dst, `{"type":"Point","coordinates":`...)
|
||||
dst = appendJSONPoint(dst, g.base, g.extra, 0)
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
dst = append(dst, '}')
|
||||
return dst
|
||||
}
|
||||
|
@ -73,6 +73,11 @@ func (g *Point) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *Point) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// String ...
|
||||
func (g *Point) String() string {
|
||||
return string(g.AppendJSON(nil))
|
||||
|
|
|
@ -52,7 +52,7 @@ func (g *Polygon) AppendJSON(dst []byte) []byte {
|
|||
}
|
||||
dst = append(dst, ']')
|
||||
if g.extra != nil {
|
||||
dst = g.extra.appendJSONExtra(dst)
|
||||
dst = g.extra.appendJSONExtra(dst, false)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
return dst
|
||||
|
@ -63,6 +63,11 @@ func (g *Polygon) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *Polygon) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// String ...
|
||||
func (g *Polygon) String() string {
|
||||
return string(g.AppendJSON(nil))
|
||||
|
|
|
@ -56,6 +56,11 @@ func (g *Rect) JSON() string {
|
|||
return string(g.AppendJSON(nil))
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (g *Rect) MarshalJSON() ([]byte, error) {
|
||||
return g.AppendJSON(nil), nil
|
||||
}
|
||||
|
||||
// String ...
|
||||
func (g *Rect) String() string {
|
||||
return string(g.AppendJSON(nil))
|
||||
|
|
Loading…
Reference in New Issue