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 / app / outline_wire.go
outline_wire.go 7.8K
1package app23import (4 "encoding/base64"5 "strconv"67 "github.com/FelineStateMachine/atlas/format/vnext"8)910const outlineWireVersion = 11112type outlineWire struct {13 Version int `json:"version"`14 ID string `json:"id"`15 Title string `json:"title"`16 Worlds []worldWire `json:"worlds"`17 Assets []assetWire `json:"assets"`18}1920type worldWire struct {21 ID string `json:"id"`22 Title string `json:"title"`23 CoordinateSpace coordinateSpaceWire `json:"coordinateSpace"`24 Claims []propertyWire `json:"claims"`25 FeatureSets []featureSetWire `json:"featureSets"`26 RasterPyramids []rasterWire `json:"rasterPyramids"`27 Presentation presentationWire `json:"presentation"`28}2930type coordinateSpaceWire struct {31 ID string `json:"id"`32 Kind string `json:"kind"`33 Unit string `json:"unit"`34 Definition string `json:"definition"`35 Extent [4]float64 `json:"extent"`36 SourceZoom string `json:"sourceZoom"`37 OriginX string `json:"originX"`38 OriginY string `json:"originY"`39 TileSize string `json:"tileSize"`40 Size string `json:"size"`41}4243type featureSetWire struct {44 ID string `json:"id"`45 Title string `json:"title"`46 SemanticType string `json:"semanticType"`47 Properties []fieldWire `json:"properties"`48 Claims []propertyWire `json:"claims"`49}5051type fieldWire struct {52 ID string `json:"id"`53 Name string `json:"name"`54 Kind vnext.Kind `json:"kind"`55 Optional bool `json:"optional"`56}5758type rasterWire struct {59 ID string `json:"id"`60 Name string `json:"name"`61 Codec string `json:"codec"`62 TileSize string `json:"tileSize"`63 MinZoom string `json:"minZoom"`64 MaxZoom string `json:"maxZoom"`65 FullZoom string `json:"fullZoom"`66 SourceZoom string `json:"sourceZoom"`67 Template string `json:"template"`68 Formats []string `json:"formats"`69 Bounds *rasterRectWire `json:"bounds,omitempty"`70 Surface *rasterRectWire `json:"surface,omitempty"`71 Interpolate bool `json:"interpolate"`72 Background string `json:"background,omitempty"`73 Shard string `json:"shard"`74 Coverage []coverageWire `json:"coverage"`75}7677type rasterRectWire struct {78 X float64 `json:"x"`79 Y float64 `json:"y"`80 Width float64 `json:"width"`81 Height float64 `json:"height"`82}8384type coverageWire struct {85 Zoom string `json:"zoom"`86 X string `json:"x"`87 Y string `json:"y"`88 W string `json:"w"`89 H string `json:"h"`90 Bits string `json:"bits"`91}9293type presentationWire struct {94 ID string `json:"id"`95 Title string `json:"title"`96 Styles []styleWire `json:"styles"`97 Layers []layerWire `json:"layers"`98 Legend []legendWire `json:"legend"`99}100101type styleWire struct {102 ID string `json:"id"`103 Symbol string `json:"symbol,omitempty"`104 Icon string `json:"icon,omitempty"`105 IconAsset string `json:"iconAsset,omitempty"`106 IconPicture bool `json:"iconPicture"`107 RenderAs string `json:"renderAs,omitempty"`108 Stroke string `json:"stroke,omitempty"`109 Fill string `json:"fill,omitempty"`110}111112type layerWire struct {113 ID string `json:"id"`114 FeatureSet string `json:"featureSet"`115 Style string `json:"style"`116 Group string `json:"group,omitempty"`117 LabelPolicy string `json:"labelPolicy,omitempty"`118 Visible bool `json:"visible"`119 Order string `json:"order"`120 MinZoom string `json:"minZoom"`121 MaxZoom string `json:"maxZoom"`122}123124type legendWire struct {125 Layer string `json:"layer"`126 Label string `json:"label"`127 Order string `json:"order"`128}129130type assetWire struct {131 ID string `json:"id"`132 MediaType string `json:"mediaType"`133 Path string `json:"path"`134 Provenance string `json:"provenance,omitempty"`135}136137func outlineResponse(volume vnext.Volume) outlineWire {138 out := outlineWire{Version: outlineWireVersion, ID: volume.ID, Title: volume.Title}139 for _, world := range volume.Worlds {140 out.Worlds = append(out.Worlds, worldResponse(world))141 }142 for _, asset := range volume.Assets {143 out.Assets = append(out.Assets, assetWire{144 ID: asset.ID, MediaType: asset.MediaType, Path: asset.Path, Provenance: asset.Provenance,145 })146 }147 return out148}149150func worldResponse(world vnext.World) worldWire {151 space := world.CoordinateSpace152 out := worldWire{153 ID: world.ID, Title: world.Title, Claims: propertyResponses(world.Claims),154 CoordinateSpace: coordinateSpaceWire{155 ID: space.ID, Kind: space.Kind, Unit: space.Unit, Definition: space.Definition, Extent: space.Extent,156 SourceZoom: intText(space.SourceZoom), OriginX: intText(space.OriginX), OriginY: intText(space.OriginY),157 TileSize: intText(space.TileSize), Size: intText(space.Size),158 },159 Presentation: presentationResponse(world.Presentation),160 }161 for _, set := range world.FeatureSets {162 out.FeatureSets = append(out.FeatureSets, featureSetResponse(set))163 }164 for _, raster := range world.RasterPyramids {165 out.RasterPyramids = append(out.RasterPyramids, rasterResponse(raster))166 }167 return out168}169170func featureSetResponse(set vnext.FeatureSet) featureSetWire {171 out := featureSetWire{172 ID: set.ID, Title: set.Title, SemanticType: set.SemanticType, Claims: propertyResponses(set.Claims),173 }174 for _, field := range set.Properties {175 out.Properties = append(out.Properties, fieldWire{176 ID: field.ID.String(), Name: field.Name, Kind: field.Kind, Optional: field.Optional,177 })178 }179 return out180}181182func rasterResponse(raster vnext.RasterPyramid) rasterWire {183 out := rasterWire{184 ID: raster.ID, Name: raster.Name, Codec: raster.Codec, TileSize: intText(raster.TileSize),185 MinZoom: intText(raster.MinZoom), MaxZoom: intText(raster.MaxZoom), FullZoom: intText(raster.FullZoom),186 SourceZoom: intText(raster.SourceZoom), Template: raster.Template, Formats: append([]string(nil), raster.Formats...),187 Interpolate: raster.Interpolate, Background: raster.Background, Shard: intText(raster.Shard),188 }189 if raster.Bounds != nil {190 out.Bounds = rectResponse(*raster.Bounds)191 }192 if raster.Surface != nil {193 out.Surface = rectResponse(*raster.Surface)194 }195 for _, coverage := range raster.Coverage {196 out.Coverage = append(out.Coverage, coverageWire{197 Zoom: intText(coverage.Zoom), X: intText(coverage.X), Y: intText(coverage.Y),198 W: intText(coverage.W), H: intText(coverage.H), Bits: base64.StdEncoding.EncodeToString(coverage.Bits),199 })200 }201 return out202}203204func rectResponse(rect vnext.RasterRect) *rasterRectWire {205 return &rasterRectWire{X: rect.X, Y: rect.Y, Width: rect.Width, Height: rect.Height}206}207208func presentationResponse(presentation vnext.Presentation) presentationWire {209 out := presentationWire{ID: presentation.ID, Title: presentation.Title}210 for _, style := range presentation.Styles {211 out.Styles = append(out.Styles, styleWire{212 ID: style.ID, Symbol: style.Symbol, Icon: style.Icon, IconAsset: style.IconAsset, IconPicture: style.IconPicture,213 RenderAs: style.RenderAs, Stroke: style.Stroke, Fill: style.Fill,214 })215 }216 for _, layer := range presentation.Layers {217 out.Layers = append(out.Layers, layerWire{218 ID: layer.ID, FeatureSet: layer.FeatureSet, Style: layer.Style, Group: layer.Group, LabelPolicy: layer.LabelPolicy,219 Visible: layer.Visible, Order: intText(layer.Order), MinZoom: intText(layer.MinZoom), MaxZoom: intText(layer.MaxZoom),220 })221 }222 for _, legend := range presentation.Legend {223 out.Legend = append(out.Legend, legendWire{Layer: legend.Layer, Label: legend.Label, Order: intText(legend.Order)})224 }225 return out226}227228func propertyResponses(properties []vnext.Property) []propertyWire {229 out := make([]propertyWire, 0, len(properties))230 for _, property := range properties {231 out = append(out, propertyResponse(property))232 }233 return out234}235236func intText(value int64) string { return strconv.FormatInt(value, 10) }