2017-03-29 20:21:59 +02:00
|
|
|
// Copyright (c) 2017 Couchbase, Inc.
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2017-03-25 01:06:14 +01:00
|
|
|
package geo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestMortonHashMortonUnhash(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
lon float64
|
|
|
|
lat float64
|
|
|
|
}{
|
|
|
|
{-180.0, -90.0},
|
|
|
|
{-5, 27.3},
|
|
|
|
{0, 0},
|
|
|
|
{1.0, 1.0},
|
|
|
|
{24.7, -80.4},
|
|
|
|
{180.0, 90.0},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
hash := MortonHash(test.lon, test.lat)
|
|
|
|
lon := MortonUnhashLon(hash)
|
|
|
|
lat := MortonUnhashLat(hash)
|
|
|
|
if compareGeo(test.lon, lon) != 0 {
|
|
|
|
t.Errorf("expected lon %f, got %f, hash %x", test.lon, lon, hash)
|
|
|
|
}
|
|
|
|
if compareGeo(test.lat, lat) != 0 {
|
|
|
|
t.Errorf("expected lat %f, got %f, hash %x", test.lat, lat, hash)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestScaleLonUnscaleLon(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
lon float64
|
|
|
|
}{
|
|
|
|
{-180.0},
|
|
|
|
{0.0},
|
|
|
|
{1.0},
|
|
|
|
{180.0},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
s := scaleLon(test.lon)
|
|
|
|
lon := unscaleLon(s)
|
|
|
|
if compareGeo(test.lon, lon) != 0 {
|
|
|
|
t.Errorf("expected %f, got %f, scaled was %d", test.lon, lon, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestScaleLatUnscaleLat(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
lat float64
|
|
|
|
}{
|
|
|
|
{-90.0},
|
|
|
|
{0.0},
|
|
|
|
{1.0},
|
|
|
|
{90.0},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
s := scaleLat(test.lat)
|
|
|
|
lat := unscaleLat(s)
|
|
|
|
if compareGeo(test.lat, lat) != 0 {
|
|
|
|
t.Errorf("expected %.16f, got %.16f, scaled was %d", test.lat, lat, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestComputeBoundingBoxCheckLatitudeAtEquator(t *testing.T) {
|
|
|
|
// at the equator 1 degree of latitude is about 110567 meters
|
|
|
|
_, upperLeftLat, _, lowerRightLat := ComputeBoundingBox(0, 0, 110567)
|
|
|
|
if math.Abs(upperLeftLat-1) > 1E-4 {
|
|
|
|
t.Errorf("expected bounding box upper left lat to be almost 1, got %f", upperLeftLat)
|
|
|
|
}
|
|
|
|
if math.Abs(lowerRightLat+1) > 1E-4 {
|
|
|
|
t.Errorf("expected bounding box lower right lat to be almost -1, got %f", lowerRightLat)
|
|
|
|
}
|
|
|
|
}
|
2017-03-29 20:21:59 +02:00
|
|
|
|
|
|
|
func TestRectIntersects(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
aMinX float64
|
|
|
|
aMinY float64
|
|
|
|
aMaxX float64
|
|
|
|
aMaxY float64
|
|
|
|
bMinX float64
|
|
|
|
bMinY float64
|
|
|
|
bMaxX float64
|
|
|
|
bMaxY float64
|
|
|
|
want bool
|
|
|
|
}{
|
|
|
|
// clearly overlap
|
|
|
|
{0, 0, 2, 2, 1, 1, 3, 3, true},
|
|
|
|
// clearly do not overalp
|
|
|
|
{0, 0, 1, 1, 2, 2, 3, 3, false},
|
|
|
|
// share common point
|
|
|
|
{0, 0, 1, 1, 1, 1, 2, 2, true},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
got := RectIntersects(test.aMinX, test.aMinY, test.aMaxX, test.aMaxY, test.bMinX, test.bMinY, test.bMaxX, test.bMaxY)
|
|
|
|
if test.want != got {
|
|
|
|
t.Errorf("expected intersects %t, got %t for %f %f %f %f %f %f %f %f", test.want, got, test.aMinX, test.aMinY, test.aMaxX, test.aMaxY, test.bMinX, test.bMinY, test.bMaxX, test.bMaxY)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRectWithin(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
aMinX float64
|
|
|
|
aMinY float64
|
|
|
|
aMaxX float64
|
|
|
|
aMaxY float64
|
|
|
|
bMinX float64
|
|
|
|
bMinY float64
|
|
|
|
bMaxX float64
|
|
|
|
bMaxY float64
|
|
|
|
want bool
|
|
|
|
}{
|
|
|
|
// clearly within
|
|
|
|
{1, 1, 2, 2, 0, 0, 3, 3, true},
|
|
|
|
// clearly not within
|
|
|
|
{0, 0, 1, 1, 2, 2, 3, 3, false},
|
|
|
|
// overlapping
|
|
|
|
{0, 0, 2, 2, 1, 1, 3, 3, false},
|
|
|
|
// share common point
|
|
|
|
{0, 0, 1, 1, 1, 1, 2, 2, false},
|
|
|
|
// within, but boxes reversed (b is within a, but not a within b)
|
|
|
|
{0, 0, 3, 3, 1, 1, 2, 2, false},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
got := RectWithin(test.aMinX, test.aMinY, test.aMaxX, test.aMaxY, test.bMinX, test.bMinY, test.bMaxX, test.bMaxY)
|
|
|
|
if test.want != got {
|
|
|
|
t.Errorf("expected within %t, got %t for %f %f %f %f %f %f %f %f", test.want, got, test.aMinX, test.aMinY, test.aMaxX, test.aMaxY, test.bMinX, test.bMinY, test.bMaxX, test.bMaxY)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBoundingBoxContains(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
lon float64
|
|
|
|
lat float64
|
|
|
|
minX float64
|
|
|
|
minY float64
|
|
|
|
maxX float64
|
|
|
|
maxY float64
|
|
|
|
want bool
|
|
|
|
}{
|
|
|
|
// clearly contains
|
|
|
|
{1, 1, 0, 0, 2, 2, true},
|
|
|
|
// clearly does not contain
|
|
|
|
{0, 0, 1, 1, 2, 2, false},
|
|
|
|
// on corner
|
|
|
|
{0, 0, 0, 0, 2, 2, true},
|
|
|
|
}
|
|
|
|
for _, test := range tests {
|
|
|
|
got := BoundingBoxContains(test.lon, test.lat, test.minX, test.minY, test.maxX, test.maxY)
|
|
|
|
if test.want != got {
|
|
|
|
t.Errorf("expected box contains %t, got %t for %f,%f in %f %f %f %f ", test.want, got, test.lon, test.lat, test.minX, test.minY, test.maxX, test.maxY)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNormalizeLon(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
lon float64
|
|
|
|
want float64
|
|
|
|
}{
|
|
|
|
{-180, -180},
|
|
|
|
{0, 0},
|
|
|
|
{180, 180},
|
|
|
|
{181, -179},
|
|
|
|
{-181, 179},
|
|
|
|
{540, 180},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
got := normalizeLon(test.lon)
|
|
|
|
if test.want != got {
|
|
|
|
t.Errorf("expected normalizedLon %f, got %f for %f", test.want, got, test.lon)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNormalizeLat(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
lat float64
|
|
|
|
want float64
|
|
|
|
}{
|
|
|
|
{-90, -90},
|
|
|
|
{0, 0},
|
|
|
|
{90, 90},
|
|
|
|
// somewhat unexpected, but double-checked against lucene
|
|
|
|
{91, 89},
|
|
|
|
{-91, -89},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
got := normalizeLat(test.lat)
|
|
|
|
if test.want != got {
|
|
|
|
t.Errorf("expected normalizedLat %f, got %f for %f", test.want, got, test.lat)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|