Improve geometry polygon searching functionality

This commit is contained in:
Tobie Morgan Hitchcock 2021-05-17 13:13:09 +01:00
parent e2635e8cf9
commit 27b80ceb8e
3 changed files with 184 additions and 0 deletions

View file

@ -28,6 +28,7 @@ import (
"github.com/abcum/surreal/cnf" "github.com/abcum/surreal/cnf"
"github.com/abcum/surreal/sql" "github.com/abcum/surreal/sql"
"github.com/abcum/surreal/util/geof"
"github.com/abcum/surreal/util/data" "github.com/abcum/surreal/util/data"
"github.com/abcum/surreal/util/deep" "github.com/abcum/surreal/util/deep"
"github.com/abcum/surreal/util/fncs" "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) 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: case bool:
switch r := r.(type) { switch r := r.(type) {
case bool: 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) return chkArray(op, l, r)
case map[string]interface{}: case map[string]interface{}:
return chkArrayL(op, l, r) return chkArrayL(op, l, r)
case *sql.Polygon:
return chkPolygonR(op, l, r)
} }
case map[string]interface{}: case map[string]interface{}:
@ -972,6 +1010,16 @@ func chkThing(op sql.Token, a, b *sql.Thing) (val bool) {
return negOp(op) 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) { func chkRegex(op sql.Token, a string, r *regexp.Regexp) (val bool) {
switch op { switch op {
case sql.EQ: case sql.EQ:
@ -1004,6 +1052,72 @@ func chkObject(op sql.Token, m map[string]interface{}, i interface{}) (val bool)
return negOp(op) 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) { func chkArrayL(op sql.Token, a []interface{}, i interface{}) (val bool) {
switch op { switch op {
case sql.EQ: case sql.EQ:

View file

@ -144,6 +144,10 @@ func ensurePoint(val interface{}) (out *sql.Point, ok bool) {
switch val := val.(type) { switch val := val.(type) {
case *sql.Point: case *sql.Point:
return val, true return val, true
case []interface{}:
if p := ensureFloats(val); len(p) == 2 {
return sql.NewPoint(p[0], p[1]), true
}
} }
return nil, false return nil, false
} }

66
util/geof/point.go Normal file
View file

@ -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
}