repo_description: Real-time Geospatial and Geofencing
repo_topics: database geo geofences geospatial index location spatial
code: package server
  
  import (
  	"strings"
  
  	"github.com/tidwall/geojson"
  )
  
  // BinaryOp represents various operators for expressions
  type BinaryOp byte
  
  // expression operator enum
  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))
  }
  