feat: 完善日月食与月掩几何链路并扩展历法接口
- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑 - 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口 - 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界 - 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名 - 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验 - 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口 - 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package svg
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
eclipsecore "b612.me/astro/eclipse"
|
||||
)
|
||||
|
||||
// 核心判定当天无日食时,三个日食图入口都必须返回 false,与 LunarEclipseMapSVG 的日期门一致;
|
||||
// 偏食足迹是“取最近一次”语义,缺这道门会把邻近日期的图当成当天的图交出去。
|
||||
func TestSolarEclipseMapSVGRequiresEclipseOnDate(t *testing.T) {
|
||||
type entry struct {
|
||||
name string
|
||||
render func(time.Time, SolarEclipseMapSVGOptions) (string, bool)
|
||||
global func(time.Time) (eclipsecore.SolarEclipseInfo, bool)
|
||||
}
|
||||
entries := []entry{
|
||||
{
|
||||
name: "default",
|
||||
render: SolarEclipseMapSVG,
|
||||
global: eclipsecore.SolarEclipseOnDateNASABulletinSplitK,
|
||||
},
|
||||
{
|
||||
name: "nasaSplitK",
|
||||
render: SolarEclipseMapSVGNASABulletinSplitK,
|
||||
global: eclipsecore.SolarEclipseOnDateNASABulletinSplitK,
|
||||
},
|
||||
{
|
||||
name: "iauSingleK",
|
||||
render: SolarEclipseMapSVGIAUSingleK,
|
||||
global: eclipsecore.SolarEclipseOnDateIAUSingleK,
|
||||
},
|
||||
}
|
||||
// 2024-04-08 是本组日期里唯一有日食的一天,其余都在最近一次日食前后半个朔望月内。
|
||||
for _, test := range []struct {
|
||||
day string
|
||||
want bool
|
||||
}{
|
||||
{day: "2024-04-08", want: true},
|
||||
{day: "2024-04-15"},
|
||||
{day: "2024-04-20"},
|
||||
{day: "2024-04-23"},
|
||||
{day: "2024-04-24"},
|
||||
} {
|
||||
date, err := time.Parse("2006-01-02", test.day)
|
||||
if err != nil {
|
||||
t.Fatalf("bad fixture date %q: %v", test.day, err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
_, coreOK := entry.global(date)
|
||||
if coreOK != test.want {
|
||||
t.Fatalf("%s %s: core eclipse gate = %v, want %v", entry.name, test.day, coreOK, test.want)
|
||||
}
|
||||
rendered, ok := entry.render(date, SolarEclipseMapSVGOptions{
|
||||
Width: 900, Height: 620, Location: time.UTC, PartialStep: 10 * time.Minute,
|
||||
})
|
||||
if ok != test.want {
|
||||
t.Fatalf("%s %s: map gate = %v, want %v (core says %v)", entry.name, test.day, ok, test.want, coreOK)
|
||||
}
|
||||
if test.want && rendered == "" {
|
||||
t.Fatalf("%s %s: gate passed but the map is empty", entry.name, test.day)
|
||||
}
|
||||
if !test.want && rendered != "" {
|
||||
t.Fatalf("%s %s: rejected date still returned %d bytes", entry.name, test.day, len(rendered))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 过了日期门的日期必须仍带完整等时线层:等时线自己还要查一次核心,门不能把它挡掉。
|
||||
func TestSolarEclipseMapOnDateKeepsIsochrones(t *testing.T) {
|
||||
rendered, ok := SolarEclipseMapSVG(
|
||||
time.Date(2024, time.April, 8, 0, 0, 0, 0, time.UTC),
|
||||
SolarEclipseMapSVGOptions{
|
||||
Width: 900, Height: 620, Location: time.UTC,
|
||||
PartialStep: 10 * time.Minute, GreatestTimeStep: 30 * time.Minute,
|
||||
},
|
||||
)
|
||||
if !ok {
|
||||
t.Fatal("2024-04-08 is an eclipse date but the map was rejected")
|
||||
}
|
||||
if got := strings.Count(rendered, `class="solar-greatest-time-isoline"`); got != 7 {
|
||||
t.Fatalf("isochrone paths = %d, want the full 7-branch fan", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user