From f7ba43360e4031be1e59be90be2fc96140122a47 Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 8 Feb 2019 13:56:07 -0800 Subject: [PATCH 1/6] Typo in error messages --- internal/server/search.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/server/search.go b/internal/server/search.go index 2788107f..9315d77a 100644 --- a/internal/server/search.go +++ b/internal/server/search.go @@ -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 == "" { From a300cb2bf8d99fd1f955602d2a9cfe9a4ac95409 Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 8 Feb 2019 13:56:43 -0800 Subject: [PATCH 2/6] Typo causing a bug in polygon clipping --- internal/clip/polygon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/clip/polygon.go b/internal/clip/polygon.go index 25de61d7..a2bebe4d 100644 --- a/internal/clip/polygon.go +++ b/internal/clip/polygon.go @@ -35,5 +35,5 @@ func clipPolygon( if newPoly.Empty() { return geojson.NewMultiPolygon(nil) } - return polygon + return newPoly } From c849ab19acb292a9b08553e1d2b20f04427f10da Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 8 Feb 2019 13:57:29 -0800 Subject: [PATCH 3/6] Implement test command --- internal/server/scripts.go | 8 +- internal/server/server.go | 2 + internal/server/test.go | 300 +++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+), 3 deletions(-) create mode 100644 internal/server/test.go diff --git a/internal/server/scripts.go b/internal/server/scripts.go index c30ad0a4..c37708ec 100644 --- a/internal/server/scripts.go +++ b/internal/server/scripts.go @@ -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() diff --git a/internal/server/server.go b/internal/server/server.go index 69471948..4f94571e 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -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 } diff --git a/internal/server/test.go b/internal/server/test.go new file mode 100644 index 00000000..6aecae33 --- /dev/null +++ b/internal/server/test.go @@ -0,0 +1,300 @@ +// TEST command: spatial tests without walking the tree. +package server + +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 = errInvalidArgument("cannot clip with " + ltyp) + 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 = errInvalidArgument("cannot clip with " + ltyp) + 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 = errInvalidArgument("cannot clip with " + ltyp) + 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("cannot clip with" + 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`) + buf.WriteString(fmt.Sprintf(`,"result":%d`, result)) + 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 +} From a3b17258c9c0d4c4a53b9a6ccc933f58400249b1 Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 8 Feb 2019 13:57:48 -0800 Subject: [PATCH 4/6] Add command description --- core/commands.json | 225 +++++++++++++++++++++++++++++++++++++++++++ core/commands_gen.go | 225 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+) diff --git a/core/commands.json b/core/commands.json index 7936a695..8fe9b3b7 100644 --- a/core/commands.json +++ b/core/commands.json @@ -1785,5 +1785,230 @@ "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": "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": "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" } } diff --git a/core/commands_gen.go b/core/commands_gen.go index fddaa3b2..5b4157a5 100644 --- a/core/commands_gen.go +++ b/core/commands_gen.go @@ -1951,5 +1951,230 @@ var commandsJSON = `{ "complexity": "O(1)", "since": "1.10.0", "group": "scripting" + }, + "TEST":{ + "summary": "Performs spatial tests", + "complexity": "One test per command, complexity depends on the test", + "arguments": [ + { + "name": "area1", + "enumargs": [ + { + "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": "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" } }` From 1cffdcc31fbdcbe733b4532f72d5ab790709e24a Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Fri, 8 Feb 2019 13:58:04 -0800 Subject: [PATCH 5/6] Add tests for the TEST comand --- tests/testcmd_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++ tests/tests_test.go | 1 + 2 files changed, 117 insertions(+) create mode 100644 tests/testcmd_test.go diff --git a/tests/testcmd_test.go b/tests/testcmd_test.go new file mode 100644 index 00000000..f2cef7af --- /dev/null +++ b/tests/testcmd_test.go @@ -0,0 +1,116 @@ +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 argument 'cannot clip with object'"}, + {"TEST", "OBJECT", poly9, "INTERSECTS", "CLIP", "CIRCLE", "1", "2", "3"}, {"ERR invalid argument 'cannot clip with circle'"}, + {"TEST", "OBJECT", poly9, "INTERSECTS", "CLIP", "GET", "mykey", "point1"}, {"ERR invalid argument 'cannot clip with get'"}, + + {"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]]]}},{"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]]]}}]}` + "]"}, + {"TEST", "OBJECT", poly101, "INTERSECTS", "CLIP", "BOUNDS", 37.73315644825698, -122.44054287672043, 37.73349585185455, -122.44008690118788}, {"0"}, + }) +} diff --git a/tests/tests_test.go b/tests/tests_test.go index 4de3d18a..f342df1b 100644 --- a/tests/tests_test.go +++ b/tests/tests_test.go @@ -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) From d179a8f2a338d244dc5289c76a15938f698acae8 Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Sun, 10 Feb 2019 14:56:11 -0800 Subject: [PATCH 6/6] Forgot to add POINT to commands.json --- core/commands.json | 26 ++++++++++++++++++++++++++ core/commands_gen.go | 28 +++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/core/commands.json b/core/commands.json index 8fe9b3b7..93adc1bb 100644 --- a/core/commands.json +++ b/core/commands.json @@ -1793,6 +1793,19 @@ { "name": "area1", "enumargs": [ + { + "name": "POINT", + "arguments": [ + { + "name": "lat", + "type": "double" + }, + { + "name": "lon", + "type": "double" + } + ] + }, { "name": "GET", "arguments": [ @@ -1910,6 +1923,19 @@ { "name": "area2", "enumargs": [ + { + "name": "POINT", + "arguments": [ + { + "name": "lat", + "type": "double" + }, + { + "name": "lon", + "type": "double" + } + ] + }, { "name": "GET", "arguments": [ diff --git a/core/commands_gen.go b/core/commands_gen.go index 5b4157a5..70e7769d 100644 --- a/core/commands_gen.go +++ b/core/commands_gen.go @@ -1953,12 +1953,25 @@ var commandsJSON = `{ "group": "scripting" }, "TEST":{ - "summary": "Performs spatial tests", + "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": [ @@ -2076,6 +2089,19 @@ var commandsJSON = `{ { "name": "area2", "enumargs": [ + { + "name": "POINT", + "arguments": [ + { + "name": "lat", + "type": "double" + }, + { + "name": "lon", + "type": "double" + } + ] + }, { "name": "GET", "arguments": [