Files
astro/moon/svg/occultation_planet_local_test.go
T
b612 9ee2163cc7 feat: 新增月掩与日月食地理绘图并提升观测计算精度
- 新增月掩恒星和行星:支持搜索、掩甚点、全球掩带及固定地点轨迹计算
- 支持恒星星表坐标转换、有限盘面行星接触事件和月掩 SVG 输出
- 新增日月食及月掩全球投影图、时间标记和 GeoJSON 地理数据接口
- 扩展日食中心线、南北界及偏食足迹采样,支持极区投影
- 修正站心时角、月出月落、月球视半径、折射和恒星自行计算
- 优化内外行星事件搜索、边界选择、极端输入处理和计算稳定性
2026-08-06 12:00:56 +08:00

166 lines
6.4 KiB
Go

package svg
import (
"errors"
"math"
"strings"
"testing"
"time"
"b612.me/astro/moon"
)
func TestFindLocalPlanetOccultationSVGsSaturnFiveContacts(t *testing.T) {
location := time.FixedZone("UTC+8", 8*3600)
diagrams, err := FindLocalPlanetOccultationSVGs(
time.Date(2025, time.February, 1, 0, 0, 0, 0, location),
time.Date(2025, time.February, 2, 0, 0, 0, 0, location),
moon.OccultationSaturn,
104.52219613, 55.25401991, 0,
moon.OccultationSearchOptions{},
LocalPlanetOccultationSVGOptions{Width: 920, Height: 700, Location: location},
)
if err != nil {
t.Fatalf("FindLocalPlanetOccultationSVGs() error = %v", err)
}
if len(diagrams) != 1 {
t.Fatalf("FindLocalPlanetOccultationSVGs() returned %d diagrams, want 1", len(diagrams))
}
diagram := diagrams[0]
for _, want := range []string{
`<svg`, `width="920"`, `height="700"`, "2025-02-01", "指定地点月掩土星",
"站心行星轨迹", "圆盘真实比例", "C1 外切始", "C2 内切始", "掩甚", "C3 内切终", "C4 外切终",
"C1-C4 接触阶段", "行星圆盘放大示意", "本地接触时刻", "白道", "土星环不参与接触计算",
`class="local-planet-track"`, `class="lunar-path-line"`, `class="overview-moon"`,
`class="overview-planet-disk"`, `<symbol id="le-moon"`, `href="#le-moon"`,
} {
if !strings.Contains(diagram, want) {
t.Fatalf("local planetary SVG missing %q", want)
}
}
if strings.Contains(diagram, "全球掩带") || strings.Contains(diagram, "全球中心线") {
t.Fatal("local planetary SVG unexpectedly contains global-path labels")
}
if got := strings.Count(diagram, `class="stage-moon"`); got != 5 {
t.Fatalf("stage Moon count = %d, want 5", got)
}
if got := strings.Count(diagram, `class="stage-planet-disk"`); got != 5 {
t.Fatalf("stage planet count = %d, want 5", got)
}
if err := validateXML(diagram); err != nil {
t.Fatalf("generated local planetary SVG is not valid XML: %v", err)
}
}
func TestLocalPlanetOccultationSVGStageCentersPreserveDisplayedTangency(t *testing.T) {
event := localPlanetOccultationEventFrame{
label: "C2",
frame: moon.PlanetOccultationDiagramFrame{
SeparationArcsec: 970,
PositionAngleDeg: 90,
},
}
x, y := localPlanetOccultationSVGStageCenter(event, 100, 100, 40, 6, 0.04)
if math.Abs(math.Hypot(x-100, y-100)-(40-6)) > 1e-12 {
t.Fatalf("C2 display distance = %.12f, want %.12f", math.Hypot(x-100, y-100), float64(40-6))
}
event.label = "C4"
x, y = localPlanetOccultationSVGStageCenter(event, 100, 100, 40, 6, 0.04)
if math.Abs(math.Hypot(x-100, y-100)-(40+6)) > 1e-12 {
t.Fatalf("C4 display distance = %.12f, want %.12f", math.Hypot(x-100, y-100), float64(40+6))
}
}
func TestLocalPlanetOccultationSVGEnglishAndCustomText(t *testing.T) {
info := localSaturnOccultation(t)
diagram, err := LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{
Language: "en",
Location: time.UTC,
Title: "Custom local planet title",
SummaryText: "Custom local planet summary",
GreatestText: "Custom local planet greatest",
OverviewTitle: "Custom local planet overview",
PhasePanelsTitle: "Custom local planet stages",
ContactsTitle: "Custom local planet contacts",
DirectionText: "Custom local planet direction",
FooterNote: "Custom local planet footer",
})
if err != nil {
t.Fatalf("LocalPlanetOccultationSVG() error = %v", err)
}
for _, want := range []string{
"Custom local planet title", "Custom local planet summary", "Custom local planet greatest",
"Custom local planet overview", "Custom local planet stages", "Custom local planet contacts",
"Custom local planet direction", "Custom local planet footer",
"C1 external ingress", "C2 internal ingress", "Greatest", "C3 internal egress", "C4 external egress",
} {
if !strings.Contains(diagram, want) {
t.Fatalf("English local planetary SVG missing %q", want)
}
}
if err := validateXML(diagram); err != nil {
t.Fatalf("English local planetary SVG is not valid XML: %v", err)
}
}
func TestLocalPlanetOccultationSVGRejectsInvalidInput(t *testing.T) {
info := localSaturnOccultation(t)
_, err := LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{Width: 1})
if !errors.Is(err, ErrInvalidLocalPlanetOccultationSVGOptions) {
t.Fatalf("invalid canvas error = %v, want ErrInvalidLocalPlanetOccultationSVGOptions", err)
}
info.ContactsComplete = false
_, err = LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{})
if !errors.Is(err, ErrInvalidLocalPlanetOccultationInfo) {
t.Fatalf("incomplete event error = %v, want ErrInvalidLocalPlanetOccultationInfo", err)
}
}
func TestLocalPlanetOccultationSVGRejectsTargetGeometryMismatch(t *testing.T) {
info := localSaturnOccultation(t)
info.Planet = moon.OccultationVenus
info.TargetID = "Venus"
_, err := LocalPlanetOccultationSVG(info, LocalPlanetOccultationSVGOptions{})
if !errors.Is(err, ErrInvalidLocalPlanetOccultationInfo) {
t.Fatalf("mismatched planet error = %v, want ErrInvalidLocalPlanetOccultationInfo", err)
}
}
func TestLocalPlanetOccultationSVGPartialEventUsesThreeStages(t *testing.T) {
events, err := moon.FindPlanetOccultations(
time.Date(2024, time.August, 21, 1, 30, 0, 0, time.UTC),
time.Date(2024, time.August, 21, 4, 0, 0, 0, time.UTC),
moon.OccultationSaturn, -30.072, -6.5, 0, moon.OccultationSearchOptions{},
)
if err != nil || len(events) != 1 {
t.Fatalf("FindPlanetOccultations() = %d events, %v; want one", len(events), err)
}
diagram, err := LocalPlanetOccultationSVG(events[0], LocalPlanetOccultationSVGOptions{Language: "en"})
if err != nil {
t.Fatalf("LocalPlanetOccultationSVG() error = %v", err)
}
if got := strings.Count(diagram, `class="stage-planet-disk"`); got != 3 {
t.Fatalf("partial event stage count = %d, want 3", got)
}
if strings.Contains(diagram, "C2 internal ingress") || strings.Contains(diagram, "C3 internal egress") {
t.Fatal("partial event SVG contains internal contacts")
}
}
func localSaturnOccultation(t *testing.T) moon.PlanetOccultationInfo {
t.Helper()
location := time.FixedZone("UTC+8", 8*3600)
events, err := moon.FindPlanetOccultations(
time.Date(2025, time.February, 1, 0, 0, 0, 0, location),
time.Date(2025, time.February, 2, 0, 0, 0, 0, location),
moon.OccultationSaturn, 104.52219613, 55.25401991, 0, moon.OccultationSearchOptions{},
)
if err != nil {
t.Fatalf("FindPlanetOccultations() error = %v", err)
}
if len(events) != 1 {
t.Fatalf("FindPlanetOccultations() returned %d events, want 1", len(events))
}
return events[0]
}