diff --git a/db/fetch.go b/db/fetch.go index 260b6ee1..990d6b7e 100644 --- a/db/fetch.go +++ b/db/fetch.go @@ -28,6 +28,7 @@ import ( "github.com/abcum/surreal/cnf" "github.com/abcum/surreal/sql" + "github.com/abcum/surreal/util/geof" "github.com/abcum/surreal/util/data" "github.com/abcum/surreal/util/deep" "github.com/abcum/surreal/util/fncs" @@ -708,6 +709,41 @@ func binaryCheck(op sql.Token, l, r, lo, ro interface{}, d *data.Doc) interface{ return chkArrayR(op, l, r) } + case *sql.Point: + switch r := r.(type) { + case *sql.Point: + return chkPoint(op, l, r) + case *sql.Polygon: + switch op { + case sql.INS: + return geof.Inside(l, r) == true + case sql.NIS: + return geof.Inside(l, r) == false + } + } + + case *sql.Polygon: + switch r := r.(type) { + case *sql.Point: + switch op { + case sql.SIN: + return geof.Contains(l, r) == true + case sql.SNI: + return geof.Contains(l, r) == false + } + case []interface{}: + if p, ok := geof.Point(r); ok { + switch op { + case sql.SIN: + return geof.Contains(l, p) == true + case sql.SNI: + return geof.Contains(l, p) == false + } + } else { + return chkPolygonL(op, l, r) + } + } + case bool: switch r := r.(type) { case bool: @@ -822,6 +858,8 @@ func binaryCheck(op sql.Token, l, r, lo, ro interface{}, d *data.Doc) interface{ return chkArray(op, l, r) case map[string]interface{}: return chkArrayL(op, l, r) + case *sql.Polygon: + return chkPolygonR(op, l, r) } case map[string]interface{}: @@ -972,6 +1010,16 @@ func chkThing(op sql.Token, a, b *sql.Thing) (val bool) { return negOp(op) } +func chkPoint(op sql.Token, a, b *sql.Point) (val bool) { + switch op { + case sql.EQ: + return a.LA == b.LA && a.LO == b.LO + case sql.NEQ: + return a.LA != b.LA || a.LO != b.LO + } + return negOp(op) +} + func chkRegex(op sql.Token, a string, r *regexp.Regexp) (val bool) { switch op { case sql.EQ: @@ -1004,6 +1052,72 @@ func chkObject(op sql.Token, m map[string]interface{}, i interface{}) (val bool) return negOp(op) } +func chkPolygonL(op sql.Token, a *sql.Polygon, b []interface{}) (val bool) { + switch op { + case sql.CONTAINSALL: + for _, v := range b { + if p, ok := geof.Point(v); ok { + if geof.Contains(a, p) == false { + return false + } + } + } + return true + case sql.CONTAINSSOME: + for _, v := range b { + if p, ok := geof.Point(v); ok { + if geof.Contains(a, p) == true { + return true + } + } + } + return false + case sql.CONTAINSNONE: + for _, v := range b { + if p, ok := geof.Point(v); ok { + if geof.Contains(a, p) == true { + return false + } + } + } + return true + } + return +} + +func chkPolygonR(op sql.Token, a []interface{}, b *sql.Polygon) (val bool) { + switch op { + case sql.ALLCONTAINEDIN: + for _, v := range a { + if p, ok := geof.Point(v); ok { + if geof.Contains(b, p) == false { + return false + } + } + } + return true + case sql.SOMECONTAINEDIN: + for _, v := range a { + if p, ok := geof.Point(v); ok { + if geof.Contains(b, p) == true { + return true + } + } + } + return false + case sql.NONECONTAINEDIN: + for _, v := range a { + if p, ok := geof.Point(v); ok { + if geof.Contains(b, p) == true { + return false + } + } + } + return true + } + return +} + func chkArrayL(op sql.Token, a []interface{}, i interface{}) (val bool) { switch op { case sql.EQ: diff --git a/util/fncs/args.go b/util/fncs/args.go index 74491d20..58d6962e 100644 --- a/util/fncs/args.go +++ b/util/fncs/args.go @@ -144,6 +144,10 @@ func ensurePoint(val interface{}) (out *sql.Point, ok bool) { switch val := val.(type) { case *sql.Point: return val, true + case []interface{}: + if p := ensureFloats(val); len(p) == 2 { + return sql.NewPoint(p[0], p[1]), true + } } return nil, false } diff --git a/util/geof/point.go b/util/geof/point.go new file mode 100644 index 00000000..969aafc6 --- /dev/null +++ b/util/geof/point.go @@ -0,0 +1,66 @@ +// Copyright © 2016 Abcum Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package geof + +import ( + "strconv" + + "github.com/abcum/surreal/sql" +) + +func ensureSlice(args interface{}) (out []interface{}, ok bool) { + if i, ok := args.([]interface{}); ok { + return i, true + } else { + return []interface{}{args}, false + } +} + +func ensureFloats(args interface{}) (out []float64) { + arr, _ := ensureSlice(args) + for _, val := range arr { + switch val := val.(type) { + case int: + out = append(out, float64(val)) + case int64: + out = append(out, float64(val)) + case uint: + out = append(out, float64(val)) + case uint64: + out = append(out, float64(val)) + case float32: + out = append(out, float64(val)) + case float64: + out = append(out, float64(val)) + case string: + if val, err := strconv.ParseFloat(val, 64); err == nil { + out = append(out, float64(val)) + } + } + } + return +} + +func Point(val interface{}) (out *sql.Point, ok bool) { + switch val := val.(type) { + case *sql.Point: + return val, true + case []interface{}: + if p := ensureFloats(val); len(p) == 2 { + return sql.NewPoint(p[0], p[1]), true + } + } + return nil, false +}