97 lines
3.1 KiB
Go
97 lines
3.1 KiB
Go
|
|
package geodata
|
||
|
|
|
||
|
|
import (
|
||
|
|
"math"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
// 裁剪入口对非有限坐标必须快速失败而不能死循环;超出 ±180 的有限经度按同一子午线归一化。
|
||
|
|
func TestTopologyRejectsNonFiniteCoordinates(t *testing.T) {
|
||
|
|
cases := []struct {
|
||
|
|
name string
|
||
|
|
points []GeoPoint
|
||
|
|
}{
|
||
|
|
{"huge-longitude", []GeoPoint{{0, 0}, {1e300, 10}, {20, 20}}},
|
||
|
|
{"positive-infinity-longitude", []GeoPoint{{0, 0}, {math.Inf(1), 10}, {20, 20}}},
|
||
|
|
{"negative-infinity-longitude", []GeoPoint{{0, 0}, {math.Inf(-1), 10}, {20, 20}}},
|
||
|
|
{"nan-longitude", []GeoPoint{{0, 0}, {math.NaN(), 10}, {20, 20}}},
|
||
|
|
{"nan-latitude", []GeoPoint{{0, 0}, {10, math.NaN()}, {20, 20}}},
|
||
|
|
{"infinity-latitude", []GeoPoint{{0, 0}, {10, math.Inf(1)}, {20, 20}}},
|
||
|
|
}
|
||
|
|
views := []ClipView{
|
||
|
|
{},
|
||
|
|
{Projection: ProjectionEquirectangular},
|
||
|
|
{Projection: ProjectionEquirectangular, Center: GeoPoint{Longitude: 100}},
|
||
|
|
{Projection: ProjectionNorthPolar},
|
||
|
|
{Projection: ProjectionSouthPolar},
|
||
|
|
{Projection: ProjectionOrthographic, Center: GeoPoint{Longitude: 104, Latitude: -1.5}},
|
||
|
|
}
|
||
|
|
for _, testCase := range cases {
|
||
|
|
wantsRejection := testCase.name != "huge-longitude"
|
||
|
|
for _, view := range views {
|
||
|
|
fragments := PolygonFragments(testCase.points, view)
|
||
|
|
segments := PolylineSegments(testCase.points, view)
|
||
|
|
if wantsRejection {
|
||
|
|
if len(fragments) != 0 || len(segments) != 0 {
|
||
|
|
t.Fatalf("%s %s: fragments=%d segments=%d, want none",
|
||
|
|
testCase.name, view.Projection, len(fragments), len(segments))
|
||
|
|
}
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
checkFinite := func(points []GeoPoint) {
|
||
|
|
for _, point := range points {
|
||
|
|
if math.IsNaN(point.Longitude) || math.IsNaN(point.Latitude) ||
|
||
|
|
math.IsInf(point.Longitude, 0) || math.IsInf(point.Latitude, 0) ||
|
||
|
|
math.Abs(point.Longitude) > 180 {
|
||
|
|
t.Fatalf("%s %s: non-finite or unnormalized point %+v", testCase.name, view.Projection, point)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for _, fragment := range fragments {
|
||
|
|
checkFinite(fragment)
|
||
|
|
}
|
||
|
|
for _, segment := range segments {
|
||
|
|
checkFinite(segment)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestTopologyNormalizesOutOfRangeLongitudeToSameMeridian(t *testing.T) {
|
||
|
|
views := []ClipView{
|
||
|
|
{},
|
||
|
|
{Projection: ProjectionEquirectangular, Center: GeoPoint{Longitude: 30}},
|
||
|
|
{Projection: ProjectionNorthPolar},
|
||
|
|
}
|
||
|
|
cases := [][2][]GeoPoint{
|
||
|
|
{
|
||
|
|
{{0, 0}, {190, 10}, {20, 20}},
|
||
|
|
{{0, 0}, {-170, 10}, {20, 20}},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
{{350, -5}, {190, 10}, {181, 20}},
|
||
|
|
{{-10, -5}, {-170, 10}, {-179, 20}},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
for _, pair := range cases {
|
||
|
|
for _, view := range views {
|
||
|
|
got := PolygonFragments(pair[0], view)
|
||
|
|
want := PolygonFragments(pair[1], view)
|
||
|
|
if len(got) != len(want) {
|
||
|
|
t.Fatalf("%s: fragment count %d, want %d", view.Projection, len(got), len(want))
|
||
|
|
}
|
||
|
|
for index := range got {
|
||
|
|
if len(got[index]) != len(want[index]) {
|
||
|
|
t.Fatalf("%s: fragment %d length %d, want %d", view.Projection, index, len(got[index]), len(want[index]))
|
||
|
|
}
|
||
|
|
for point := range got[index] {
|
||
|
|
if got[index][point] != want[index][point] {
|
||
|
|
t.Fatalf("%s: fragment %d point %d = %+v, want %+v",
|
||
|
|
view.Projection, index, point, got[index][point], want[index][point])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|