Add geometry contains + inside database functions
This commit is contained in:
parent
ef160763f3
commit
02bacf121c
6 changed files with 143 additions and 5 deletions
|
@ -97,6 +97,7 @@ var funcs = map[string]map[int]interface{}{
|
|||
"geo.point": {1: nil, 2: nil},
|
||||
"geo.circle": {2: nil},
|
||||
"geo.polygon": {-1: nil},
|
||||
"geo.contains": {2: nil},
|
||||
"geo.distance": {2: nil},
|
||||
"geo.inside": {2: nil},
|
||||
"geo.intersects": {2: nil},
|
||||
|
|
|
@ -70,6 +70,8 @@ func Run(ctx context.Context, name string, args ...interface{}) (interface{}, er
|
|||
return geoCircle(ctx, args...)
|
||||
case "geo.polygon":
|
||||
return geoPolygon(ctx, args...)
|
||||
case "geo.contains":
|
||||
return geoContains(ctx, args...)
|
||||
case "geo.distance":
|
||||
return geoDistance(ctx, args...)
|
||||
case "geo.inside":
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package fncs
|
||||
|
||||
import (
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/abcum/surreal/sql"
|
||||
|
@ -57,8 +58,22 @@ func geoCircle(ctx context.Context, args ...interface{}) (interface{}, error) {
|
|||
|
||||
func geoPolygon(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
switch len(args) {
|
||||
case 0, 1, 2:
|
||||
case 0, 2:
|
||||
// Not enough arguments, so just ignore
|
||||
case 1:
|
||||
var pnts []*sql.Point
|
||||
if a, ok := ensureSlice(args[0]); ok {
|
||||
for _, a := range a {
|
||||
if p, _ := ensurePoint(a); p != nil {
|
||||
pnts = append(pnts, p)
|
||||
} else if p := ensureFloats(a); len(p) == 2 {
|
||||
pnts = append(pnts, sql.NewPoint(p[0], p[1]))
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return sql.NewPolygon(pnts...), nil
|
||||
}
|
||||
default:
|
||||
var pnts []*sql.Point
|
||||
for _, a := range args {
|
||||
|
@ -75,6 +90,15 @@ func geoPolygon(ctx context.Context, args ...interface{}) (interface{}, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func geoContains(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
if a, ok := ensurePolygon(args[0]); ok {
|
||||
if b, ok := ensurePoint(args[1]); ok {
|
||||
return geof.Contains(a, b), nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func geoDistance(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
if pnt, ok := ensurePoint(args[0]); ok {
|
||||
if frm, ok := ensurePoint(args[1]); ok {
|
||||
|
@ -85,8 +109,8 @@ func geoDistance(ctx context.Context, args ...interface{}) (interface{}, error)
|
|||
}
|
||||
|
||||
func geoInside(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
if a, ok := ensureGeometry(args[0]); ok {
|
||||
if b, ok := ensureGeometry(args[1]); ok {
|
||||
if a, ok := ensurePoint(args[0]); ok {
|
||||
if b, ok := ensurePolygon(args[1]); ok {
|
||||
return geof.Inside(a, b), nil
|
||||
}
|
||||
}
|
||||
|
|
31
util/geof/contains.go
Normal file
31
util/geof/contains.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
// 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 (
|
||||
"github.com/abcum/surreal/sql"
|
||||
)
|
||||
|
||||
func Contains(a *sql.Polygon, b *sql.Point) bool {
|
||||
beg := len(a.PS) - 1
|
||||
end := 0
|
||||
contains := raycast(b, a.PS[beg], a.PS[end])
|
||||
for i := 1; i < len(a.PS); i++ {
|
||||
if raycast(b, a.PS[i-1], a.PS[i]) {
|
||||
contains = !contains
|
||||
}
|
||||
}
|
||||
return contains
|
||||
}
|
|
@ -14,6 +14,18 @@
|
|||
|
||||
package geof
|
||||
|
||||
func Inside(a, b interface{}) bool {
|
||||
return false
|
||||
import (
|
||||
"github.com/abcum/surreal/sql"
|
||||
)
|
||||
|
||||
func Inside(a *sql.Point, b *sql.Polygon) bool {
|
||||
beg := len(b.PS) - 1
|
||||
end := 0
|
||||
contains := raycast(a, b.PS[beg], b.PS[end])
|
||||
for i := 1; i < len(b.PS); i++ {
|
||||
if raycast(a, b.PS[i-1], b.PS[i]) {
|
||||
contains = !contains
|
||||
}
|
||||
}
|
||||
return contains
|
||||
}
|
||||
|
|
68
util/geof/raycast.go
Normal file
68
util/geof/raycast.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
// 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 (
|
||||
"math"
|
||||
|
||||
"github.com/abcum/surreal/sql"
|
||||
)
|
||||
|
||||
func raycast(point, beg, end *sql.Point) bool {
|
||||
|
||||
// Always ensure that the the first point
|
||||
// has a Y coordinate that is less than
|
||||
// the second point. Switch if not.
|
||||
|
||||
if beg.LO > end.LO {
|
||||
beg, end = end, beg
|
||||
}
|
||||
|
||||
// Move the point's Y coordinate outside of
|
||||
// the bounds of the testing region so that
|
||||
// we can start drawing a ray
|
||||
for point.LO == beg.LO || point.LO == end.LO {
|
||||
lng := math.Nextafter(point.LO, math.Inf(1))
|
||||
point = sql.NewPoint(point.LA, lng)
|
||||
}
|
||||
|
||||
// If we are outside of the polygon, indicate so.
|
||||
if point.LO < beg.LO || point.LO > end.LO {
|
||||
return false
|
||||
}
|
||||
|
||||
if beg.LA > end.LA {
|
||||
if point.LA > beg.LA {
|
||||
return false
|
||||
}
|
||||
if point.LA < end.LA {
|
||||
return true
|
||||
}
|
||||
|
||||
} else {
|
||||
if point.LA > end.LA {
|
||||
return false
|
||||
}
|
||||
if point.LA < beg.LA {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
raySlope := (point.LO - beg.LO) / (point.LA - beg.LA)
|
||||
diagSlope := (end.LO - beg.LO) / (end.LA - beg.LA)
|
||||
|
||||
return raySlope >= diagSlope
|
||||
|
||||
}
|
Loading…
Reference in a new issue