Files
astro/internal/geodata/nonfinite_test.go
T
b612 2bf8478639 feat: 完善日月食与月掩几何链路并扩展历法接口
- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑
- 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口
- 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界
- 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名
- 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验
- 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口
- 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
2026-09-17 12:27:40 +08:00

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])
}
}
}
}
}
}