clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

feat/atlas-next-horizon
atlas / internal / authoring / us_profile_test.go
us_profile_test.go 9.2K
1package authoring23import (4 "bytes"5 "fmt"6 "strings"7 "testing"89 "gopkg.in/yaml.v3"10)1112func TestUSAreaProjectBuildsTheNationwideDefaultContract(t *testing.T) {13 profile := DefaultUSAreaProfile()14 profile.Bounds = [4]float64{-105.1, 39.6, -104.9, 39.8}15 profile.DetailZoom = 121617 project, summary, err := NewUSAreaProject(profile)18 if err != nil {19 t.Fatal(err)20 }21 if err := project.Validate(); err != nil {22 t.Fatalf("generated project is invalid: %v", err)23 }24 if project.ID != "sample-region" || project.Title != "Sample Region" {25 t.Fatalf("project identity = %q / %q", project.ID, project.Title)26 }27 space := project.Target.CoordinateSpace28 if space.Definition != "atlas:tile-plane" || space.SourceZoom != 12 || space.Size != summary.PixelSize {29 t.Fatalf("coordinate space = %+v, summary = %+v", space, summary)30 }31 if space.Extent != [4]float64{0, 0, float64(space.Size), float64(space.Size)} {32 t.Fatalf("coordinate extent = %v", space.Extent)33 }34 if len(project.Rasters) != 1 || project.Rasters[0].Adapter != "raster-file" || !strings.Contains(project.Rasters[0].Locator, "USGSTopo/MapServer/export") {35 t.Fatalf("topographic raster = %+v", project.Rasters)36 }37 if len(project.Sources) != 6 {38 t.Fatalf("feature sources = %d, want roads, hydro, and counties", len(project.Sources))39 }40 for _, source := range project.Sources {41 mapped := make(map[string]string, len(source.Mapping.Properties))42 for _, property := range source.Mapping.Properties {43 mapped[property.Field] = property.Source44 }45 switch source.ID {46 case "primary-roads", "secondary-roads", "local-roads":47 if source.Mapping.Identity != "properties.OID" || source.Query.ObjectID != "OBJECTID" || mapped["route-type"] != "properties.RTTYP" {48 t.Errorf("road identity or mapping = %+v", source)49 }50 case "water-bodies":51 if mapped["water-area"] != "properties.AREAWATER" {52 t.Errorf("water body mapping = %+v", mapped)53 }54 case "counties":55 if mapped["land-area"] != "properties.AREALAND" || mapped["water-area"] != "properties.AREAWATER" {56 t.Errorf("county mapping = %+v", mapped)57 }58 }59 }60 wantSets := map[string]string{"roads": "path", "waterways": "path", "water-bodies": "area", "jurisdictions": "area"}61 for _, set := range project.FeatureSets {62 if wantSets[set.ID] != set.Geometry {63 t.Errorf("feature set %s geometry = %q", set.ID, set.Geometry)64 }65 delete(wantSets, set.ID)66 }67 if len(wantSets) != 0 {68 t.Fatalf("missing feature sets: %v", wantSets)69 }70 if summary.CaptureRequests != len(project.Sources)+1 || summary.RasterTiles < 1 || summary.EstimatedBytes < 1 {71 t.Fatalf("summary = %+v", summary)72 }73 volume, err := assemble(project, nil)74 if err != nil {75 t.Fatalf("assemble presentation: %v", err)76 }77 presentation := volume.Worlds[0].Presentation78 if len(presentation.Legend) != len(project.Presentation.Layers) || len(presentation.Legend) == 0 {79 t.Fatalf("compiled legend = %+v for layers %+v", presentation.Legend, project.Presentation.Layers)80 }81 plan, err := PlanProject(project, nil, t.TempDir())82 if err != nil {83 t.Fatalf("generated project does not plan: %v", err)84 }85 if plan.RasterTiles != summary.RasterTiles || len(plan.Requests) != summary.CaptureRequests {86 t.Fatalf("plan = %d tiles / %d requests, summary = %+v", plan.RasterTiles, len(plan.Requests), summary)87 }88}8990func TestUSAreaProjectCanSelectSourceFamiliesWithoutLosingTypedPresentation(t *testing.T) {91 profile := DefaultUSAreaProfile()92 profile.Bounds = [4]float64{-105.1, 39.6, -104.9, 39.8}93 profile.DetailZoom = 1094 profile.IncludeHydro = false95 profile.IncludeCounties = false96 profile.Presentation.RoadLabel = "Routes"97 profile.Presentation.RoadColor = "#c05030"98 profile.Presentation.RoadVisible = false99 profile.Presentation.RoadOrder = 17100101 project, _, err := NewUSAreaProject(profile)102 if err != nil {103 t.Fatal(err)104 }105 if len(project.FeatureSets) != 1 || project.FeatureSets[0].ID != "roads" {106 t.Fatalf("selected feature sets = %+v", project.FeatureSets)107 }108 if len(project.Presentation.Layers) != 1 || project.Presentation.Layers[0].FeatureSet != "roads" {109 t.Fatalf("selected presentation = %+v", project.Presentation.Layers)110 }111 layer, style := project.Presentation.Layers[0], project.Presentation.Styles[0]112 if layer.Label != "Routes" || layer.Visible || layer.Order != 17 || style.Stroke != "#c05030" {113 t.Fatalf("creator presentation choices were lost: layer=%+v style=%+v", layer, style)114 }115 for _, source := range project.Sources {116 if source.Mapping.FeatureSet != "roads" || source.Query.SpatialReference != 3857 || source.Query.ObjectID == "" {117 t.Fatalf("road source = %+v", source)118 }119 }120}121122func TestUSAreaProjectSupportsAlaskaAndHawaii(t *testing.T) {123 tests := []struct {124 name string125 bounds [4]float64126 }{127 {name: "Alaska", bounds: [4]float64{-151.0, 60.9, -150.7, 61.2}},128 {name: "Hawaii", bounds: [4]float64{-157.95, 21.25, -157.7, 21.45}},129 }130 for _, tt := range tests {131 t.Run(tt.name, func(t *testing.T) {132 profile := DefaultUSAreaProfile()133 profile.Bounds, profile.DetailZoom = tt.bounds, 11134 project, _, err := NewUSAreaProject(profile)135 if err != nil {136 t.Fatal(err)137 }138 if err := project.Validate(); err != nil {139 t.Fatalf("generated project is invalid: %v", err)140 }141 })142 }143}144145func TestAreaProjectSelectionIsGeographicRatherThanSourceBounded(t *testing.T) {146 for name, bounds := range map[string][4]float64{147 "Paris": {2.28, 48.82, 2.42, 48.91},148 "Sydney": {151.14, -33.92, 151.26, -33.82},149 } {150 t.Run(name, func(t *testing.T) {151 profile := DefaultAreaProfile()152 profile.Bounds, profile.DetailZoom = bounds, 11153 if _, _, err := NewAreaProject(profile); err != nil {154 t.Fatalf("an arbitrary geographic selection was coupled to source coverage: %v", err)155 }156 })157 }158}159160func TestUSAreaProjectCarriesCreatorOwnedIdentity(t *testing.T) {161 profile := DefaultUSAreaProfile()162 profile.ID = "my-region"163 profile.Title = "My Region"164 project, _, err := NewUSAreaProject(profile)165 if err != nil {166 t.Fatal(err)167 }168 if project.ID != "my-region" || project.Title != "My Region" || project.Target.World != "my-region" || project.Target.Title != "My Region" {169 t.Fatalf("creator identity was not carried: %+v", project)170 }171 if project.Target.CoordinateSpace.ID != "my-region-grid" || project.Presentation.Title != "My Region" {172 t.Fatalf("dependent identity was not carried: target=%+v presentation=%+v", project.Target, project.Presentation)173 }174}175176func TestUSRoadLayersDoNotFuseSharedPagingObjectIDs(t *testing.T) {177 profile := DefaultUSAreaProfile()178 profile.Bounds = [4]float64{-105.1, 39.6, -104.9, 39.8}179 profile.DetailZoom = 10180 profile.IncludeTopo, profile.IncludeHydro, profile.IncludeCounties = false, false, false181 project, _, err := NewUSAreaProject(profile)182 if err != nil {183 t.Fatal(err)184 }185 var observations []observation186 for index, source := range project.Sources[:2] {187 extent, err := sourceExtent(source.Mapping.Geometry, project.Target.CoordinateSpace)188 if err != nil {189 t.Fatal(err)190 }191 x, y := (extent[0]+extent[2])/2, (extent[1]+extent[3])/2192 body := []byte(fmt.Sprintf(`{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"OBJECTID":7,"OID":"road-%d","NAME":"Road %d","MTFCC":"S1100","RTTYP":"I"},"geometry":{"type":"LineString","coordinates":[[%f,%f],[%f,%f]]}}]}`,193 index, index, x, y, x+1, y+1))194 seen, err := observe(project, index, source, Capture{}, body)195 if err != nil {196 t.Fatal(err)197 }198 observations = append(observations, seen...)199 }200 volume, err := assemble(project, observations)201 if err != nil {202 t.Fatal(err)203 }204 if got := len(volume.Worlds[0].FeatureSets[0].Features); got != 2 {205 t.Fatalf("two road layers sharing OBJECTID fused into %d feature(s)", got)206 }207}208209func TestUSAreaProjectRefusesOutOfBoundsOrExplosiveSelections(t *testing.T) {210 tests := []struct {211 name string212 bounds [4]float64213 zoom int214 message string215 }{216 {name: "outside Web Mercator", bounds: [4]float64{2, 85, 3, 89}, zoom: 10, message: "Web Mercator world"},217 {name: "empty", bounds: [4]float64{-105, 40, -105, 40}, zoom: 10, message: "west < east"},218 {name: "too detailed", bounds: [4]float64{-124, 25, -67, 49}, zoom: 16, message: "4096"},219 }220 for _, tt := range tests {221 t.Run(tt.name, func(t *testing.T) {222 profile := DefaultUSAreaProfile()223 profile.Bounds, profile.DetailZoom = tt.bounds, tt.zoom224 if _, _, err := NewUSAreaProject(profile); err == nil || !strings.Contains(err.Error(), tt.message) {225 t.Fatalf("error = %v, want %q", err, tt.message)226 }227 })228 }229}230231func TestMarshalProjectYAMLRoundTripsStrictly(t *testing.T) {232 profile := DefaultUSAreaProfile()233 profile.Bounds = [4]float64{-105.1, 39.6, -104.9, 39.8}234 profile.DetailZoom = 11235 project, _, err := NewUSAreaProject(profile)236 if err != nil {237 t.Fatal(err)238 }239 data, err := MarshalProjectYAML(project)240 if err != nil {241 t.Fatal(err)242 }243 decoder := yaml.NewDecoder(bytes.NewReader(data))244 decoder.KnownFields(true)245 var restored Project246 if err := decoder.Decode(&restored); err != nil {247 t.Fatalf("strict decode: %v\n%s", err, data)248 }249 if err := restored.Validate(); err != nil {250 t.Fatalf("round trip: %v\n%s", err, data)251 }252 if !strings.Contains(string(data), "title: Sample Region") || !strings.Contains(string(data), "origin-x:") || !strings.Contains(string(data), "origin-y:") {253 t.Fatalf("portable manifest misses neutral identity or tile origins:\n%s", data)254 }255}