1package arcgismap23import (4 "encoding/json"5 "reflect"6 "testing"7)89// clipWindow is a plain unit-square window: edges on whole degrees, so every10// expected crossing is exact.11var clipWindow = Window{West: 0, South: 0, East: 1, North: 1}1213func ring(positions ...[2]float64) [][]float64 {14 out := make([][]float64, 0, len(positions)+1)15 for _, p := range positions {16 out = append(out, []float64{p[0], p[1]})17 }18 return append(out, []float64{positions[0][0], positions[0][1]})19}2021func TestClipRingsKeepsAnInsideRing(t *testing.T) {22 inside := [][][][]float64{{ring(23 [2]float64{0.2, 0.2}, [2]float64{0.8, 0.2}, [2]float64{0.8, 0.8}, [2]float64{0.2, 0.8},24 )}}25 got := ClipRings(clipWindow, inside)26 if !reflect.DeepEqual(got, inside) {27 t.Fatalf("an inside ring changed: %v", got)28 }29}3031func TestClipRingsCutsACrossingRing(t *testing.T) {32 crossing := [][][][]float64{{ring(33 [2]float64{0.5, 0.5}, [2]float64{1.5, 0.5}, [2]float64{1.5, 0.7}, [2]float64{0.5, 0.7},34 )}}35 got := ClipRings(clipWindow, crossing)36 if len(got) != 1 || len(got[0]) != 1 {37 t.Fatalf("crossing ring clipped to %v", got)38 }39 want := ring([2]float64{0.5, 0.5}, [2]float64{1, 0.5}, [2]float64{1, 0.7}, [2]float64{0.5, 0.7})40 if !reflect.DeepEqual(got[0][0], want) {41 t.Fatalf("crossing ring clipped to %v, want %v", got[0][0], want)42 }43}4445func TestClipRingsDropsAnOutsideRing(t *testing.T) {46 outside := [][][][]float64{{ring(47 [2]float64{2, 2}, [2]float64{3, 2}, [2]float64{3, 3}, [2]float64{2, 3},48 )}}49 if got := ClipRings(clipWindow, outside); got != nil {50 t.Fatalf("an outside ring survived: %v", got)51 }52}5354func TestClipRingsKeepsAHole(t *testing.T) {55 holed := [][][][]float64{{56 ring([2]float64{0.1, 0.1}, [2]float64{0.9, 0.1}, [2]float64{0.9, 0.9}, [2]float64{0.1, 0.9}),57 ring([2]float64{0.4, 0.4}, [2]float64{0.6, 0.4}, [2]float64{0.6, 0.6}, [2]float64{0.4, 0.6}),58 }}59 got := ClipRings(clipWindow, holed)60 if len(got) != 1 || len(got[0]) != 2 {61 t.Fatalf("the hole did not survive: %v", got)62 }63}6465func TestClipRingsDropsAPolygonWhoseGroundIsGone(t *testing.T) {66 // The outer ring misses the window even though its hole would clip.67 gone := [][][][]float64{{68 ring([2]float64{2, 2}, [2]float64{3, 2}, [2]float64{3, 3}, [2]float64{2, 3}),69 ring([2]float64{0.4, 0.4}, [2]float64{0.6, 0.4}, [2]float64{0.6, 0.6}, [2]float64{0.4, 0.6}),70 }}71 if got := ClipRings(clipWindow, gone); got != nil {72 t.Fatalf("a polygon without ground survived: %v", got)73 }74}7576func TestClipRingsCoversTheWindowCorner(t *testing.T) {77 // A huge ring swallowing the whole window clips to the window itself.78 swallowing := [][][][]float64{{ring(79 [2]float64{-5, -5}, [2]float64{5, -5}, [2]float64{5, 5}, [2]float64{-5, 5},80 )}}81 got := ClipRings(clipWindow, swallowing)82 if len(got) != 1 || len(got[0]) != 1 || len(got[0][0]) != 5 {83 t.Fatalf("the window itself should remain, got %v", got)84 }85}8687func TestClipLinesSplitsAtTheWindow(t *testing.T) {88 // In at the west, out at the east, back in through the north: two parts.89 zigzag := [][][]float64{{90 {-0.5, 0.5}, {0.5, 0.5}, {1.5, 0.5}, {1.5, 2}, {0.5, 2}, {0.5, 0.9},91 }}92 got := ClipLines(clipWindow, zigzag)93 want := [][][]float64{94 {{0, 0.5}, {0.5, 0.5}, {1, 0.5}},95 {{0.5, 1}, {0.5, 0.9}},96 }97 if !reflect.DeepEqual(got, want) {98 t.Fatalf("zigzag clipped to %v, want %v", got, want)99 }100}101102func TestClipLinesKeepsAnInsideLine(t *testing.T) {103 inside := [][][]float64{{{0.1, 0.1}, {0.5, 0.5}, {0.9, 0.2}}}104 got := ClipLines(clipWindow, inside)105 if !reflect.DeepEqual(got, inside) {106 t.Fatalf("an inside line changed: %v", got)107 }108}109110func TestClipLinesDropsAnOutsideLine(t *testing.T) {111 outside := [][][]float64{{{2, 2}, {3, 3}}}112 if got := ClipLines(clipWindow, outside); got != nil {113 t.Fatalf("an outside line survived: %v", got)114 }115}116117// Clipping the same shape twice spells the same bytes, which is what capture118// determinism asks of every transformation.119func TestClipIsDeterministic(t *testing.T) {120 shape := [][][][]float64{{ring(121 [2]float64{0.123456789, -0.4}, [2]float64{1.7, 0.3}, [2]float64{0.6, 1.9},122 )}}123 first, err := json.Marshal(ClipRings(clipWindow, shape))124 if err != nil {125 t.Fatal(err)126 }127 second, err := json.Marshal(ClipRings(clipWindow, [][][][]float64{{ring(128 [2]float64{0.123456789, -0.4}, [2]float64{1.7, 0.3}, [2]float64{0.6, 1.9},129 )}}))130 if err != nil {131 t.Fatal(err)132 }133 if string(first) != string(second) {134 t.Fatalf("clipping is not deterministic: %s vs %s", first, second)135 }136 again := ClipRings(clipWindow, ClipRings(clipWindow, shape))137 raw, err := json.Marshal(again)138 if err != nil {139 t.Fatal(err)140 }141 if string(raw) != string(first) {142 t.Fatalf("clipping is not idempotent: %s vs %s", raw, first)143 }144}