package svg import ( "strconv" "strings" "testing" "time" "b612.me/astro/internal/svgmap" ) func TestSolarEclipseMapSVGTotalIncludesPartialAndCentralRegions(t *testing.T) { diagram, ok := SolarEclipseMapSVG( time.Date(2024, 4, 8, 0, 0, 0, 0, time.UTC), SolarEclipseMapSVGOptions{Width: 900, Height: 620, Location: time.UTC, PartialStep: 10 * time.Minute}, ) if !ok { t.Fatal("expected total solar-eclipse map") } for _, want := range []string{ "日全食全球见食图", "偏食始", "偏食终", "偏食可见区", "全食带", "中心线", "全球见食范围与中心食带", "全球阶段", "中心食始", "中心食终", "食带宽", "太阳沙罗 139", "食甚点太阳高度", "中心食持续", "P2", "P3", "U1", "U4", `class="partial-eclipse-region"`, `class="central-eclipse-band"`, `class="solar-center-line"`, `class="northern-central-limit"`, `class="southern-central-limit"`, `class="solar-greatest-marker"`, `class="solar-global-events"`, `class="solar-time-marker"`, `>17:00`, `class="solar-greatest-terminator"`, `class="solar-penumbral-outline"`, `class="solar-central-shadow-outline"`, `class="solar-shadow-contact solar-contact-p1"`, `class="solar-axis-contact"`, `class="solar-subsolar-marker"`, `class="land"`, "不含行政边界", } { if !strings.Contains(diagram, want) { t.Fatalf("total solar-eclipse map missing %q", want) } } if err := validateEclipseMapXML(diagram); err != nil { t.Fatalf("total solar-eclipse map is not valid XML: %v", err) } } func TestSolarEclipseMapSVGPartialOnlyUsesPolarProjection(t *testing.T) { diagram, ok := SolarEclipseMapSVG( time.Date(2025, 3, 29, 0, 0, 0, 0, time.UTC), SolarEclipseMapSVGOptions{PartialStep: 10 * time.Minute}, ) if !ok { t.Fatal("expected partial solar-eclipse map") } for _, want := range []string{"日偏食全球见食图", `class="partial-eclipse-region"`, ` 0 { polygons = append(polygons, current) } current = nil index++ case "L": index++ case "Z": if len(current) > 0 { polygons = append(polygons, current) current = nil } index++ default: if index+1 >= len(fields) { t.Fatalf("incomplete SVG coordinate at token %d", index) } x, err := strconv.ParseFloat(fields[index], 64) if err != nil { t.Fatalf("invalid SVG x coordinate %q: %v", fields[index], err) } y, err := strconv.ParseFloat(fields[index+1], 64) if err != nil { t.Fatalf("invalid SVG y coordinate %q: %v", fields[index+1], err) } current = append(current, [2]float64{x, y}) index += 2 } } if len(current) > 0 { polygons = append(polygons, current) } return polygons } func solarSVGPointInPolygon(x, y float64, polygon [][2]float64) bool { inside := false for current, previous := 0, len(polygon)-1; current < len(polygon); previous, current = current, current+1 { a, b := polygon[current], polygon[previous] if (a[1] > y) != (b[1] > y) && x < (b[0]-a[0])*(y-a[1])/(b[1]-a[1])+a[0] { inside = !inside } } return inside }