Files
astro/eclipse/svg/transit_text_overlap_test.go
b612 ccf5880269 feat: 完善水星与金星全球凌日搜索及制图导出
- 基于平均下合周期与节点相位预筛候选,减少无效星历计算;跳过无法求解的单个会合周期,避免有限覆盖星历导致搜索提前终止
- 引入影轴 γ、半影半径及地球尺度几何判定,结合站心可见性识别地心无接触、仅部分地区可见的掠凌
- 添加金星 243 年、水星 46 年周期族编号,并明确编号适用范围及未分配时的零值语义
- 为 SVG、GeoJSON 和 KML 增加站心部分凌日可见区,适配接触等时线、几何元数据、图层样式及取景;为地心无接触凌日事件增加正射图,按可见区球面面积加权居中
2026-10-20 22:35:39 +08:00

127 lines
5.1 KiB
Go

package svg
import (
"fmt"
"regexp"
"strings"
"testing"
"time"
)
var transitLegendSwatchPattern = regexp.MustCompile(`<rect [^>]*width="18" height="9"[^>]*fill-opacity="([0-9.]+)"`)
type transitOverlapCase struct {
name string
render func(width, height int) (string, bool)
}
func transitOverlapCases() []transitOverlapCase {
partial := time.Date(1937, time.May, 11, 0, 0, 0, 0, time.UTC)
ordinary := time.Date(2012, time.June, 6, 0, 0, 0, 0, time.UTC)
return []transitOverlapCase{
{"local-partial-zh", func(width, height int) (string, bool) {
return TransitLocalSVG(TransitMercury, partial, 0, -60, 0, TransitLocalSVGOptions{Width: width, Height: height})
}},
{"local-partial-en", func(width, height int) (string, bool) {
return TransitLocalSVG(TransitMercury, partial, 0, -60, 0, TransitLocalSVGOptions{Width: width, Height: height, Language: "en"})
}},
{"local-full-zh", func(width, height int) (string, bool) {
return TransitLocalSVG(TransitVenus, ordinary, 116.4, 39.9, 0, TransitLocalSVGOptions{Width: width, Height: height})
}},
{"local-full-en", func(width, height int) (string, bool) {
return TransitLocalSVG(TransitVenus, ordinary, 116.4, 39.9, 0, TransitLocalSVGOptions{Width: width, Height: height, Language: "en"})
}},
{"map-partial", func(width, height int) (string, bool) {
return TransitMapSVG(TransitMercury, partial, TransitMapSVGOptions{Width: width, Height: height, Isochrones: TransitIsochroneAll})
}},
{"map-partial-orthographic", func(width, height int) (string, bool) {
return TransitMapSVG(TransitMercury, partial, TransitMapSVGOptions{
Width: width, Height: height, Isochrones: TransitIsochroneAll, Projection: EclipseMapProjectionOrthographic,
})
}},
{"map-ordinary", func(width, height int) (string, bool) {
return TransitMapSVG(TransitVenus, ordinary, TransitMapSVGOptions{Width: width, Height: height, Isochrones: TransitIsochroneAll})
}},
}
}
// 凌日图与日月食图同一口径:任意两个尺寸下返回的图都必须 0 文字压叠。
func TestTransitChartTextOverlapMatrix(t *testing.T) {
for _, size := range [][2]int{{640, 420}, {640, 480}, {800, 600}, {960, 640}, {960, 680}, {1200, 800}} {
for _, test := range transitOverlapCases() {
name := fmt.Sprintf("%s/%dx%d", test.name, size[0], size[1])
document, ok := test.render(size[0], size[1])
if !ok {
continue
}
assertNoTextOverlap(t, name, document)
}
}
// 文档下限尺寸必须继续出图,拒绝只允许出现在真放不下的画布上。
if _, ok := TransitLocalSVG(TransitMercury, time.Date(1937, time.May, 11, 0, 0, 0, 0, time.UTC), 0, -60, 0,
TransitLocalSVGOptions{Width: 640, Height: 480}); !ok {
t.Error("documented 640x480 local transit canvas was rejected")
}
}
// 地心无接触的可见区填充必须落在 data-source 词表内,消费方才能判断该层来源。
func TestTransitPartialRegionsUseDocumentedDataSource(t *testing.T) {
chart, ok := TransitMapSVG(TransitMercury, time.Date(1937, time.May, 11, 0, 0, 0, 0, time.UTC), TransitMapSVGOptions{})
if !ok {
t.Fatal("partial transit render failed")
}
if !strings.Contains(chart, `data-source="`+eclipseMapSourceTransitPartialVisibility+`"`) {
t.Fatalf("partial regions carry no documented data-source marker")
}
for _, match := range eclipseMapDataSourcePattern.FindAllStringSubmatch(chart, -1) {
known := false
for _, source := range eclipseMapDataSources {
known = known || source == match[1]
}
if !known {
t.Fatalf("undocumented data-source %q", match[1])
}
}
}
// 图例色块画在白底上取月食图同值;部分凌日单块可见区比普通分区更浓。
func TestTransitMapFillOpacities(t *testing.T) {
for _, test := range []struct {
planet TransitPlanet
date time.Time
pattern *regexp.Regexp
region float64
}{
{TransitVenus, time.Date(2012, time.June, 6, 0, 0, 0, 0, time.UTC),
regexp.MustCompile(`class="transit-[a-z-]+-region"[^>]*fill-opacity="([0-9.]+)"`), transitMapRegionFillOpacity},
{TransitMercury, time.Date(1937, time.May, 11, 0, 0, 0, 0, time.UTC),
regexp.MustCompile(`class="transit-partial-region"[^>]*fill-opacity="([0-9.]+)"`), transitMapPartialFillOpacity},
} {
chart, ok := TransitMapSVG(test.planet, test.date, TransitMapSVGOptions{})
if !ok {
t.Fatalf("%v: render failed", test.date)
}
regions := test.pattern.FindAllStringSubmatch(chart, -1)
if len(regions) == 0 {
t.Fatalf("%v: no region fill found", test.date)
}
for _, match := range regions {
if match[1] != fmt.Sprintf("%.2f", test.region) {
t.Fatalf("%v: region fill-opacity %s, want %.2f", test.date, match[1], test.region)
}
}
legends := transitLegendSwatchPattern.FindAllStringSubmatch(chart, -1)
if len(legends) == 0 {
t.Fatalf("%v: no legend swatch found", test.date)
}
for _, match := range legends {
if match[1] != fmt.Sprintf("%.2f", transitMapLegendFillOpacity) {
t.Fatalf("%v: legend fill-opacity %s, want %.2f", test.date, match[1], transitMapLegendFillOpacity)
}
}
}
if transitMapPartialFillOpacity <= transitMapRegionFillOpacity {
t.Fatal("partial-transit footprint must be denser than ordinary regions")
}
}