Merge pull request #464 from rshura/area-expression
Add area expressions.
This commit is contained in:
commit
df477bf3f4
186
internal/server/expression.go
Normal file
186
internal/server/expression.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tidwall/geojson"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BinaryOp byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
NOOP BinaryOp = iota
|
||||||
|
AND
|
||||||
|
OR
|
||||||
|
tokenAND = "and"
|
||||||
|
tokenOR = "or"
|
||||||
|
tokenNOT = "not"
|
||||||
|
tokenLParen = "("
|
||||||
|
tokenRParen = ")"
|
||||||
|
)
|
||||||
|
|
||||||
|
// areaExpression is (maybe negated) either an spatial object or operator + children (other expressions).
|
||||||
|
type areaExpression struct {
|
||||||
|
negate bool
|
||||||
|
obj geojson.Object
|
||||||
|
op BinaryOp
|
||||||
|
children children
|
||||||
|
}
|
||||||
|
|
||||||
|
type children []*areaExpression
|
||||||
|
|
||||||
|
// String representation, helpful in logging.
|
||||||
|
func (e *areaExpression) String() (res string) {
|
||||||
|
if e.obj != nil {
|
||||||
|
res = e.obj.String()
|
||||||
|
} else {
|
||||||
|
var chStrings []string
|
||||||
|
for _, c := range e.children {
|
||||||
|
chStrings = append(chStrings, c.String())
|
||||||
|
}
|
||||||
|
switch e.op {
|
||||||
|
case NOOP:
|
||||||
|
res = "empty operator"
|
||||||
|
case AND:
|
||||||
|
res = "(" + strings.Join(chStrings, " "+tokenAND+" ") + ")"
|
||||||
|
case OR:
|
||||||
|
res = "(" + strings.Join(chStrings, " "+tokenOR+" ") + ")"
|
||||||
|
default:
|
||||||
|
res = "unknown operator"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e.negate {
|
||||||
|
res = tokenNOT + " " + res
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return boolean value modulo negate field of the expression.
|
||||||
|
func (e *areaExpression) maybeNegate(val bool) bool {
|
||||||
|
if e.negate {
|
||||||
|
return !val
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods for testing an areaExpression against the spatial object.
|
||||||
|
func (e *areaExpression) testObject(
|
||||||
|
o geojson.Object,
|
||||||
|
objObjTest func(o1, o2 geojson.Object) bool,
|
||||||
|
exprObjTest func(ae *areaExpression, ob geojson.Object) bool,
|
||||||
|
) bool {
|
||||||
|
if e.obj != nil {
|
||||||
|
return objObjTest(e.obj, o)
|
||||||
|
}
|
||||||
|
switch e.op {
|
||||||
|
case AND:
|
||||||
|
for _, c := range e.children {
|
||||||
|
if !exprObjTest(c, o) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case OR:
|
||||||
|
for _, c := range e.children {
|
||||||
|
if exprObjTest(c, o) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) rawIntersects(o geojson.Object) bool {
|
||||||
|
return e.testObject(o, geojson.Object.Intersects, (*areaExpression).Intersects)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) rawContains(o geojson.Object) bool {
|
||||||
|
return e.testObject(o, geojson.Object.Contains, (*areaExpression).Contains)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) rawWithin(o geojson.Object) bool {
|
||||||
|
return e.testObject(o, geojson.Object.Within, (*areaExpression).Within)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) Intersects(o geojson.Object) bool {
|
||||||
|
return e.maybeNegate(e.rawIntersects(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) Contains(o geojson.Object) bool {
|
||||||
|
return e.maybeNegate(e.rawContains(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) Within(o geojson.Object) bool {
|
||||||
|
return e.maybeNegate(e.rawWithin(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods for testing an areaExpression against another areaExpression.
|
||||||
|
func (e *areaExpression) testExpression(
|
||||||
|
other *areaExpression,
|
||||||
|
exprObjTest func(ae *areaExpression, ob geojson.Object) bool,
|
||||||
|
rawExprExprTest func(ae1, ae2 *areaExpression) bool,
|
||||||
|
exprExprTest func(ae1, ae2 *areaExpression) bool,
|
||||||
|
) bool {
|
||||||
|
if other.negate {
|
||||||
|
oppositeExp := &areaExpression{negate: !e.negate, obj: e.obj, op: e.op, children: e.children}
|
||||||
|
nonNegateOther := &areaExpression{obj: other.obj, op: other.op, children: other.children}
|
||||||
|
return exprExprTest(oppositeExp, nonNegateOther)
|
||||||
|
}
|
||||||
|
if other.obj != nil {
|
||||||
|
return exprObjTest(e, other.obj)
|
||||||
|
}
|
||||||
|
switch other.op {
|
||||||
|
case AND:
|
||||||
|
for _, c := range other.children {
|
||||||
|
if !rawExprExprTest(e, c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case OR:
|
||||||
|
for _, c := range other.children {
|
||||||
|
if rawExprExprTest(e, c) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) rawIntersectsExpr(other *areaExpression) bool {
|
||||||
|
return e.testExpression(
|
||||||
|
other,
|
||||||
|
(*areaExpression).rawIntersects,
|
||||||
|
(*areaExpression).rawIntersectsExpr,
|
||||||
|
(*areaExpression).IntersectsExpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) rawWithinExpr(other *areaExpression) bool {
|
||||||
|
return e.testExpression(
|
||||||
|
other,
|
||||||
|
(*areaExpression).rawWithin,
|
||||||
|
(*areaExpression).rawWithinExpr,
|
||||||
|
(*areaExpression).WithinExpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) rawContainsExpr(other *areaExpression) bool {
|
||||||
|
return e.testExpression(
|
||||||
|
other,
|
||||||
|
(*areaExpression).rawContains,
|
||||||
|
(*areaExpression).rawContainsExpr,
|
||||||
|
(*areaExpression).ContainsExpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) IntersectsExpr(other *areaExpression) bool {
|
||||||
|
return e.maybeNegate(e.rawIntersectsExpr(other))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) WithinExpr(other *areaExpression) bool {
|
||||||
|
return e.maybeNegate(e.rawWithinExpr(other))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *areaExpression) ContainsExpr(other *areaExpression) bool {
|
||||||
|
return e.maybeNegate(e.rawContainsExpr(other))
|
||||||
|
}
|
@ -231,8 +231,9 @@ func (s *Server) cmdTest(msg *Message) (res resp.Value, err error) {
|
|||||||
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var test string
|
var test string
|
||||||
var obj1, obj2, clipped geojson.Object
|
var clipped geojson.Object
|
||||||
if vs, obj1, err = s.parseArea(vs, false); err != nil {
|
var area1, area2 *areaExpression
|
||||||
|
if vs, area1, err = s.parseAreaExpression(vs, false); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if vs, test, ok = tokenval(vs); !ok || test == "" {
|
if vs, test, ok = tokenval(vs); !ok || test == "" {
|
||||||
@ -259,7 +260,11 @@ func (s *Server) cmdTest(msg *Message) (res resp.Value, err error) {
|
|||||||
doClip = true
|
doClip = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if vs, obj2, err = s.parseArea(vs, doClip); err != nil {
|
if vs, area2, err = s.parseAreaExpression(vs, doClip); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if doClip && (area1.obj == nil || area2.obj == nil) {
|
||||||
|
err = errInvalidArgument("clip")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(vs) != 0 {
|
if len(vs) != 0 {
|
||||||
@ -268,14 +273,14 @@ func (s *Server) cmdTest(msg *Message) (res resp.Value, err error) {
|
|||||||
|
|
||||||
var result int
|
var result int
|
||||||
if lTest == "within" {
|
if lTest == "within" {
|
||||||
if obj1.Within(obj2) {
|
if area1.WithinExpr(area2) {
|
||||||
result = 1
|
result = 1
|
||||||
}
|
}
|
||||||
} else if lTest == "intersects" {
|
} else if lTest == "intersects" {
|
||||||
if obj1.Intersects(obj2) {
|
if area1.IntersectsExpr(area2) {
|
||||||
result = 1
|
result = 1
|
||||||
if doClip {
|
if doClip {
|
||||||
clipped = clip.Clip(obj1, obj2)
|
clipped = clip.Clip(area1.obj, area2.obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,3 +688,140 @@ func (c *Server) parseSearchScanBaseTokens(
|
|||||||
tout = t
|
tout = t
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type parentStack []*areaExpression
|
||||||
|
|
||||||
|
func (ps *parentStack) isEmpty() bool {
|
||||||
|
return len(*ps) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *parentStack) push(e *areaExpression) {
|
||||||
|
*ps = append(*ps, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *parentStack) pop() (e *areaExpression, empty bool) {
|
||||||
|
n := len(*ps)
|
||||||
|
if n == 0 {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
x := (*ps)[n-1]
|
||||||
|
*ps = (*ps)[:n-1]
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) parseAreaExpression(vsin []string, doClip bool) (vsout []string, ae *areaExpression, err error) {
|
||||||
|
ps := &parentStack{}
|
||||||
|
vsout = vsin[:]
|
||||||
|
var negate, needObj bool
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
nvs, wtok, ok := tokenval(vsout)
|
||||||
|
if !ok || len(wtok) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch strings.ToLower(wtok) {
|
||||||
|
case tokenLParen:
|
||||||
|
newExpr := &areaExpression{negate: negate, op: NOOP}
|
||||||
|
negate = false
|
||||||
|
needObj = false
|
||||||
|
if ae != nil {
|
||||||
|
ae.children = append(ae.children, newExpr)
|
||||||
|
}
|
||||||
|
ae = newExpr
|
||||||
|
ps.push(ae)
|
||||||
|
vsout = nvs
|
||||||
|
case tokenRParen:
|
||||||
|
if needObj {
|
||||||
|
err = errInvalidArgument(tokenRParen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if parent, empty := ps.pop(); empty {
|
||||||
|
err = errInvalidArgument(tokenRParen)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
ae = parent
|
||||||
|
}
|
||||||
|
vsout = nvs
|
||||||
|
case tokenNOT:
|
||||||
|
negate = !negate
|
||||||
|
needObj = true
|
||||||
|
vsout = nvs
|
||||||
|
case tokenAND:
|
||||||
|
if needObj {
|
||||||
|
err = errInvalidArgument(tokenAND)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
needObj = true
|
||||||
|
if ae == nil {
|
||||||
|
err = errInvalidArgument(tokenAND)
|
||||||
|
return
|
||||||
|
} else if ae.obj == nil {
|
||||||
|
switch ae.op {
|
||||||
|
case OR:
|
||||||
|
numChildren := len(ae.children)
|
||||||
|
if numChildren < 2 {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
ae.children = append(
|
||||||
|
ae.children[:numChildren-1],
|
||||||
|
&areaExpression{
|
||||||
|
op: AND,
|
||||||
|
children: []*areaExpression{ae.children[numChildren-1]}})
|
||||||
|
}
|
||||||
|
case NOOP:
|
||||||
|
ae.op = AND
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ae = &areaExpression{op: AND, children: []*areaExpression{ae}}
|
||||||
|
}
|
||||||
|
vsout = nvs
|
||||||
|
case tokenOR:
|
||||||
|
if needObj {
|
||||||
|
err = errInvalidArgument(tokenOR)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
needObj = true
|
||||||
|
if ae == nil {
|
||||||
|
err = errInvalidArgument(tokenOR)
|
||||||
|
return
|
||||||
|
} else if ae.obj == nil {
|
||||||
|
switch ae.op {
|
||||||
|
case AND:
|
||||||
|
if len(ae.children) < 2 {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
ae = &areaExpression{op: OR, children: []*areaExpression{ae}}
|
||||||
|
}
|
||||||
|
case NOOP:
|
||||||
|
ae.op = OR
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ae = &areaExpression{op: OR, children: []*areaExpression{ae}}
|
||||||
|
}
|
||||||
|
vsout = nvs
|
||||||
|
case "point", "circle", "object", "bounds", "hash", "quadkey", "tile", "get":
|
||||||
|
if parsedVs, parsedObj, areaErr := s.parseArea(vsout, doClip); areaErr != nil {
|
||||||
|
err = areaErr
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
newExpr := &areaExpression{negate: negate, obj: parsedObj, op: NOOP}
|
||||||
|
negate = false
|
||||||
|
needObj = false
|
||||||
|
if ae == nil {
|
||||||
|
ae = newExpr
|
||||||
|
} else {
|
||||||
|
ae.children = append(ae.children, newExpr)
|
||||||
|
}
|
||||||
|
vsout = parsedVs
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ps.isEmpty() || needObj || ae == nil || (ae.obj == nil && len(ae.children) == 0) {
|
||||||
|
err = errInvalidNumberOfArguments
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -8,6 +8,8 @@ func subTestTestCmd(t *testing.T, mc *mockServer) {
|
|||||||
runStep(t, mc, "WITHIN", testcmd_WITHIN_test)
|
runStep(t, mc, "WITHIN", testcmd_WITHIN_test)
|
||||||
runStep(t, mc, "INTERSECTS", testcmd_INTERSECTS_test)
|
runStep(t, mc, "INTERSECTS", testcmd_INTERSECTS_test)
|
||||||
runStep(t, mc, "INTERSECTS_CLIP", testcmd_INTERSECTS_CLIP_test)
|
runStep(t, mc, "INTERSECTS_CLIP", testcmd_INTERSECTS_CLIP_test)
|
||||||
|
runStep(t, mc, "ExpressionErrors", testcmd_expressionErrors_test)
|
||||||
|
runStep(t, mc, "Expressions", testcmd_expression_test)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testcmd_WITHIN_test(mc *mockServer) error {
|
func testcmd_WITHIN_test(mc *mockServer) error {
|
||||||
@ -115,3 +117,97 @@ func testcmd_INTERSECTS_CLIP_test(mc *mockServer) error {
|
|||||||
{"TEST", "OBJECT", poly101, "INTERSECTS", "CLIP", "BOUNDS", 37.73315644825698, -122.44054287672043, 37.73349585185455, -122.44008690118788}, {"0"},
|
{"TEST", "OBJECT", poly101, "INTERSECTS", "CLIP", "BOUNDS", 37.73315644825698, -122.44054287672043, 37.73349585185455, -122.44008690118788}, {"0"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testcmd_expressionErrors_test(mc *mockServer) error {
|
||||||
|
return mc.DoBatch([][]interface{}{
|
||||||
|
{"SET", "mykey", "foo", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
|
||||||
|
{"SET", "mykey", "bar", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
|
||||||
|
{"SET", "mykey", "baz", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
|
||||||
|
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "(", "GET", "mykey", "bar"}, {
|
||||||
|
"ERR wrong number of arguments for 'test' command"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", ")"}, {
|
||||||
|
"ERR invalid argument ')'"},
|
||||||
|
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "OR", "GET", "mykey", "bar"}, {
|
||||||
|
"ERR invalid argument 'or'"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "AND", "GET", "mykey", "bar"}, {
|
||||||
|
"ERR invalid argument 'and'"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "OR", "AND", "GET", "mykey", "baz"}, {
|
||||||
|
"ERR invalid argument 'and'"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "AND", "OR", "GET", "mykey", "baz"}, {
|
||||||
|
"ERR invalid argument 'or'"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "OR", "OR", "GET", "mykey", "baz"}, {
|
||||||
|
"ERR invalid argument 'or'"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "AND", "AND", "GET", "mykey", "baz"}, {
|
||||||
|
"ERR invalid argument 'and'"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "OR"}, {
|
||||||
|
"ERR wrong number of arguments for 'test' command"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "AND"}, {
|
||||||
|
"ERR wrong number of arguments for 'test' command"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "NOT"}, {
|
||||||
|
"ERR wrong number of arguments for 'test' command"},
|
||||||
|
{"TEST", "GET", "mykey", "foo", "INTERSECTS", "GET", "mykey", "bar", "NOT", "AND", "GET", "mykey", "baz"}, {
|
||||||
|
"ERR invalid argument 'and'"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testcmd_expression_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]]]}`
|
||||||
|
|
||||||
|
return mc.DoBatch([][]interface{}{
|
||||||
|
{"SET", "mykey", "line3", "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
|
||||||
|
{"SET", "mykey", "poly8", "OBJECT", poly8}, {"OK"},
|
||||||
|
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "NOT", "OBJECT", poly}, {"0"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "NOT", "NOT", "OBJECT", poly}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "NOT", "NOT", "NOT", "OBJECT", poly}, {"0"},
|
||||||
|
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "OBJECT", poly8, "OR", "OBJECT", poly}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "OBJECT", poly8, "AND", "OBJECT", poly}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "poly8", "OR", "OBJECT", poly}, {"1"},
|
||||||
|
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "line3"}, {"0"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "poly8", "AND",
|
||||||
|
"(", "OBJECT", poly, "AND", "GET", "mykey", "line3", ")"}, {"0"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "poly8", "AND",
|
||||||
|
"(", "OBJECT", poly, "OR", "GET", "mykey", "line3", ")"}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "poly8", "AND",
|
||||||
|
"(", "OBJECT", poly, "AND", "NOT", "GET", "mykey", "line3", ")"}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "NOT", "GET", "mykey", "line3"}, {"1"},
|
||||||
|
{"TEST", "NOT", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "line3"}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "line3",
|
||||||
|
"OR", "OBJECT", poly8, "AND", "OBJECT", poly}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "OBJECT", poly8, "AND", "OBJECT", poly,
|
||||||
|
"OR", "GET", "mykey", "line3"}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS", "GET", "mykey", "line3", "OR",
|
||||||
|
"(", "OBJECT", poly8, "AND", "OBJECT", poly, ")"}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "INTERSECTS",
|
||||||
|
"(", "GET", "mykey", "line3", "OR", "OBJECT", poly8, ")", "AND", "OBJECT", poly}, {"1"},
|
||||||
|
|
||||||
|
{"TEST", "OBJECT", poly9, "WITHIN", "OBJECT", poly8, "OR", "OBJECT", poly}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "WITHIN", "OBJECT", poly8, "AND", "OBJECT", poly}, {"1"},
|
||||||
|
|
||||||
|
{"TEST", "OBJECT", poly9, "WITHIN", "GET", "mykey", "line3"}, {"0"},
|
||||||
|
{"TEST", "OBJECT", poly9, "WITHIN", "GET", "mykey", "poly8", "AND",
|
||||||
|
"(", "OBJECT", poly, "AND", "GET", "mykey", "line3", ")"}, {"0"},
|
||||||
|
{"TEST", "OBJECT", poly9, "WITHIN", "GET", "mykey", "poly8", "AND",
|
||||||
|
"(", "OBJECT", poly, "OR", "GET", "mykey", "line3", ")"}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "WITHIN", "GET", "mykey", "poly8", "AND",
|
||||||
|
"(", "OBJECT", poly, "AND", "NOT", "GET", "mykey", "line3", ")"}, {"1"},
|
||||||
|
{"TEST", "OBJECT", poly9, "WITHIN", "NOT", "GET", "mykey", "line3"}, {"1"},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user