package svgmap import ( "fmt" "math" "strings" "testing" ) func TestPolarProjectionPlacesPoleAtCenterAndEquatorOnFrame(t *testing.T) { frame := Frame{X: 10, Y: 20, Width: 300, Height: 300, Projection: ProjectionNorthPolar} x, y, ok := frame.Project(123, 90) if !ok || math.Abs(x-160) > 1e-9 || math.Abs(y-170) > 1e-9 { t.Fatalf("north pole = %.3f %.3f %v, want map center", x, y, ok) } // 事件经线朝下,中心经线在赤道上落在圆盘最下方。 x, y, ok = frame.Project(0, 0) if !ok || math.Abs(x-160) > 1e-9 || math.Abs(y-320) > 1e-9 { t.Fatalf("prime-meridian equator = %.3f %.3f %v, want bottom edge", x, y, ok) } if _, _, ok := frame.Project(0, -1); ok { t.Fatal("north-polar projection accepted a southern-hemisphere point") } } func TestProjectionClippingDoesNotSplitPolarAntimeridian(t *testing.T) { points := []GeoPoint{{Longitude: 170, Latitude: 70}, {Longitude: -170, Latitude: 70}} segments := PolylineSegments(points, ClipView{Projection: ProjectionNorthPolar}) if len(segments) != 1 || len(segments[0]) != 2 { t.Fatalf("polar antimeridian segments = %#v, want one continuous segment", segments) } segments = PolylineSegments(points, ClipView{Projection: ProjectionEquirectangular}) if len(segments) != 2 { t.Fatalf("equirectangular antimeridian segment count = %d, want 2", len(segments)) } } func TestNaturalEarthAssetsAndPolarClip(t *testing.T) { if len(equirectangularLandPath) < 150000 || len(northPolarLandPath) < 100000 || len(southPolarLandPath) < 40000 { t.Fatalf("unexpected embedded map sizes: world=%d north=%d south=%d", len(equirectangularLandPath), len(northPolarLandPath), len(southPolarLandPath)) } frame := Frame{Width: 300, Height: 300, Projection: ProjectionSouthPolar} var builder strings.Builder frame.WriteOcean(&builder) frame.WriteLand(&builder, "clip") frame.WriteFrame(&builder) for _, want := range []string{`class="map-ocean"`, `class="land"`, `class="map-frame"`, ` 1e-9 { t.Fatalf("pole projects to y=%.3f, want centre %.3f", poleY, centreY) } // 事件经线朝下:赤道上中心经线的点落在圆盘最下方。 _, meridianY, _ := frame.Project(0, 0) if meridianY <= centreY { t.Fatalf("centre meridian projects to y=%.1f, want below centre %.1f", meridianY, centreY) } // 事件附近东在右:同纬度上偏东的点横坐标更大。 west, _, _ := frame.Project(-10, 45) east, _, _ := frame.Project(10, 45) if east <= west { t.Fatalf("10E projects to x=%.1f, want right of 10W at %.1f", east, west) } } func TestCenteredEquirectangularLandCoversWholeFrame(t *testing.T) { for _, center := range []float64{-179.5, -104.145, -90, -1, 1, 90, 104.145, 179.5} { frame := Frame{ X: 57, Y: 184, Width: 360, Height: 180, Projection: ProjectionEquirectangular, CenterLongitude: center, } var builder strings.Builder frame.WriteLand(&builder, "clip") offsets := centeredLandTileOffsets(t, builder.String()) if len(offsets) == 0 { t.Fatalf("center %.3f drew no land tiles", center) } left, right := math.Inf(1), math.Inf(-1) for _, offset := range offsets { left = math.Min(left, offset) right = math.Max(right, offset+frame.Width) } if left > frame.X+1e-6 || right < frame.X+frame.Width-1e-6 { t.Fatalf("center %.3f covers longitude band [%.3f,%.3f], frame needs [%.3f,%.3f]", center, left, right, frame.X, frame.X+frame.Width) } } } func centeredLandTileOffsets(t *testing.T, document string) []float64 { t.Helper() offsets := make([]float64, 0, 2) for _, piece := range strings.Split(document, `