2019-06-11 00:47:42 +03:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/tidwall/geojson"
|
|
|
|
)
|
|
|
|
|
|
|
|
type BinaryOp byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
NOOP BinaryOp = iota
|
|
|
|
AND
|
|
|
|
OR
|
2019-06-12 03:13:33 +03:00
|
|
|
tokenAND = "and"
|
|
|
|
tokenOR = "or"
|
|
|
|
tokenNOT = "not"
|
|
|
|
tokenLParen = "("
|
|
|
|
tokenRParen = ")"
|
2019-06-11 00:47:42 +03:00
|
|
|
)
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
// areaExpression is (maybe negated) either an spatial object or operator + children (other expressions).
|
2019-06-11 00:47:42 +03:00
|
|
|
type areaExpression struct {
|
2019-06-13 20:56:33 +03:00
|
|
|
negate bool
|
|
|
|
obj geojson.Object
|
|
|
|
op BinaryOp
|
2019-06-11 00:47:42 +03:00
|
|
|
children children
|
|
|
|
}
|
|
|
|
|
|
|
|
type children []*areaExpression
|
|
|
|
|
2019-06-13 20:56:33 +03:00
|
|
|
// String representation, helpful in logging.
|
2019-06-12 03:13:33 +03:00
|
|
|
func (e *areaExpression) String() (res string) {
|
2019-06-11 00:47:42 +03:00
|
|
|
if e.obj != nil {
|
2019-06-12 03:13:33 +03:00
|
|
|
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"
|
|
|
|
}
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
if e.negate {
|
|
|
|
res = tokenNOT + " " + res
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
return
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return boolean value modulo negate field of the expression.
|
2019-06-12 03:13:33 +03:00
|
|
|
func (e *areaExpression) maybeNegate(val bool) bool {
|
2019-06-11 00:47:42 +03:00
|
|
|
if e.negate {
|
|
|
|
return !val
|
|
|
|
}
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
// Methods for testing an areaExpression against the spatial object.
|
2019-06-13 20:56:33 +03:00
|
|
|
func (e *areaExpression) testObject(
|
|
|
|
o geojson.Object,
|
|
|
|
objObjTest func(o1, o2 geojson.Object) bool,
|
|
|
|
exprObjTest func(ae *areaExpression, ob geojson.Object) bool,
|
|
|
|
) bool {
|
2019-06-11 00:47:42 +03:00
|
|
|
if e.obj != nil {
|
2019-06-13 20:56:33 +03:00
|
|
|
return objObjTest(e.obj, o)
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
switch e.op {
|
|
|
|
case AND:
|
|
|
|
for _, c := range e.children {
|
2019-06-13 20:56:33 +03:00
|
|
|
if !exprObjTest(c, o) {
|
2019-06-12 03:13:33 +03:00
|
|
|
return false
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
return true
|
2019-06-11 00:47:42 +03:00
|
|
|
case OR:
|
|
|
|
for _, c := range e.children {
|
2019-06-13 20:56:33 +03:00
|
|
|
if exprObjTest(c, o) {
|
2019-06-12 03:13:33 +03:00
|
|
|
return true
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
return false
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
return false
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
|
2019-06-13 20:56:33 +03:00
|
|
|
func (e *areaExpression) rawIntersects(o geojson.Object) bool {
|
|
|
|
return e.testObject(o, geojson.Object.Intersects, (*areaExpression).Intersects)
|
|
|
|
}
|
|
|
|
|
2019-06-12 03:13:33 +03:00
|
|
|
func (e *areaExpression) rawContains(o geojson.Object) bool {
|
2019-06-13 20:56:33 +03:00
|
|
|
return e.testObject(o, geojson.Object.Contains, (*areaExpression).Contains)
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
|
2019-06-12 03:13:33 +03:00
|
|
|
func (e *areaExpression) rawWithin(o geojson.Object) bool {
|
2019-06-13 20:56:33 +03:00
|
|
|
return e.testObject(o, geojson.Object.Within, (*areaExpression).Within)
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
|
2019-06-12 03:13:33 +03:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
// Methods for testing an areaExpression against another areaExpression.
|
2019-06-13 20:56:33 +03:00
|
|
|
func (e *areaExpression) testExpression(
|
2019-06-13 23:33:07 +03:00
|
|
|
other *areaExpression,
|
2019-06-13 20:56:33 +03:00
|
|
|
exprObjTest func(ae *areaExpression, ob geojson.Object) bool,
|
|
|
|
rawExprExprTest func(ae1, ae2 *areaExpression) bool,
|
|
|
|
exprExprTest func(ae1, ae2 *areaExpression) bool,
|
|
|
|
) bool {
|
2019-06-13 23:33:07 +03:00
|
|
|
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)
|
2019-06-12 03:13:33 +03:00
|
|
|
}
|
2019-06-13 23:33:07 +03:00
|
|
|
if other.obj != nil {
|
|
|
|
return exprObjTest(e, other.obj)
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
2019-06-13 23:33:07 +03:00
|
|
|
switch other.op {
|
2019-06-11 00:47:42 +03:00
|
|
|
case AND:
|
2019-06-13 23:33:07 +03:00
|
|
|
for _, c := range other.children {
|
2019-06-13 20:56:33 +03:00
|
|
|
if !rawExprExprTest(e, c) {
|
2019-06-12 03:13:33 +03:00
|
|
|
return false
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
return true
|
2019-06-11 00:47:42 +03:00
|
|
|
case OR:
|
2019-06-13 23:33:07 +03:00
|
|
|
for _, c := range other.children {
|
2019-06-13 20:56:33 +03:00
|
|
|
if rawExprExprTest(e, c) {
|
2019-06-12 03:13:33 +03:00
|
|
|
return true
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
return false
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
2019-06-12 03:13:33 +03:00
|
|
|
return false
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
func (e *areaExpression) rawIntersectsExpr(other *areaExpression) bool {
|
2019-06-13 20:56:33 +03:00
|
|
|
return e.testExpression(
|
2019-06-13 23:33:07 +03:00
|
|
|
other,
|
2019-06-13 20:56:33 +03:00
|
|
|
(*areaExpression).rawIntersects,
|
|
|
|
(*areaExpression).rawIntersectsExpr,
|
|
|
|
(*areaExpression).IntersectsExpr)
|
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
func (e *areaExpression) rawWithinExpr(other *areaExpression) bool {
|
2019-06-13 20:56:33 +03:00
|
|
|
return e.testExpression(
|
2019-06-13 23:33:07 +03:00
|
|
|
other,
|
2019-06-13 20:56:33 +03:00
|
|
|
(*areaExpression).rawWithin,
|
|
|
|
(*areaExpression).rawWithinExpr,
|
|
|
|
(*areaExpression).WithinExpr)
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
func (e *areaExpression) rawContainsExpr(other *areaExpression) bool {
|
2019-06-13 20:56:33 +03:00
|
|
|
return e.testExpression(
|
2019-06-13 23:33:07 +03:00
|
|
|
other,
|
2019-06-13 20:56:33 +03:00
|
|
|
(*areaExpression).rawContains,
|
|
|
|
(*areaExpression).rawContainsExpr,
|
|
|
|
(*areaExpression).ContainsExpr)
|
2019-06-12 03:13:33 +03:00
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
func (e *areaExpression) IntersectsExpr(other *areaExpression) bool {
|
|
|
|
return e.maybeNegate(e.rawIntersectsExpr(other))
|
2019-06-12 03:13:33 +03:00
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
func (e *areaExpression) WithinExpr(other *areaExpression) bool {
|
|
|
|
return e.maybeNegate(e.rawWithinExpr(other))
|
2019-06-12 03:13:33 +03:00
|
|
|
}
|
|
|
|
|
2019-06-13 23:33:07 +03:00
|
|
|
func (e *areaExpression) ContainsExpr(other *areaExpression) bool {
|
|
|
|
return e.maybeNegate(e.rawContainsExpr(other))
|
2019-06-11 00:47:42 +03:00
|
|
|
}
|