Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 22 additions & 25 deletions internal/maptool/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import (

// mapJSON is the structure written to map.json.
type mapJSON struct {
Name string `json:"name"`
WorldSize int `json:"worldSize"`
ImageSize int `json:"imageSize"`
Multiplier int `json:"multiplier"`
MaxZoom int `json:"maxZoom"`
MinZoom int `json:"minZoom"`
MaplibreStyle string `json:"maplibreStyle"`
Name string `json:"name"`
WorldSize int `json:"worldSize"`
ImageSize int `json:"imageSize"`
Multiplier int `json:"multiplier"`
MaxZoom int `json:"maxZoom"`
MinZoom int `json:"minZoom"`
Maplibre bool `json:"maplibre,omitempty"`
}

// assetPath joins a URL prefix with a filename. If prefix is empty, returns filename as-is.
Expand Down Expand Up @@ -101,12 +101,11 @@ func NewGenerateStylesStage() Stage {
Name: "generate_styles",
Run: func(ctx context.Context, job *Job) error {
worldName := job.WorldName
basePrefix := "images/maps/" + worldName

tilesPrefix := basePrefix
tilesPrefix := "images/maps/" + worldName
stylesDir := job.OutputDir
if job.SubDirs {
tilesPrefix = basePrefix + "/tiles"
tilesPrefix += "/tiles"
stylesDir = job.StylesOutputDir()
if err := os.MkdirAll(stylesDir, 0755); err != nil {
return fmt.Errorf("create styles dir: %w", err)
Expand All @@ -127,16 +126,19 @@ func NewGenerateStylesStage() Stage {
variant StyleVariant
filename string
}{
{StyleStandard, "standard.json"},
{StyleTopo, "topo.json"},
{StyleSatellite, "satellite.json"},
{StyleHybrid, "hybrid.json"},
{StyleColorRelief, "color-relief.json"},
}

for _, v := range variants {
styleDoc := GenerateStyleDocument(styleCfg, v.variant)
if err := writeJSON(filepath.Join(stylesDir, v.filename), styleDoc); err != nil {
return fmt.Errorf("write %s: %w", v.filename, err)
}
}
job.HasMaplibre = true

return nil
},
Expand All @@ -152,26 +154,21 @@ func NewGenerateGradMehMetadataStage() Stage {
Name: "generate_metadata",
Run: func(ctx context.Context, job *Job) error {
worldName := job.WorldName
basePrefix := "images/maps/" + worldName

stylesPrefix := basePrefix
if job.SubDirs {
stylesPrefix = basePrefix + "/styles"
}

// 1. Generate map.json (OCAP2 web compat) — points to standard style
// 1. Generate map.json (OCAP2 web compat)
maxZoom := job.MaxZoom
if maxZoom == 0 {
maxZoom = 6
}

doc := mapJSON{
Name: worldName,
WorldSize: job.WorldSize,
ImageSize: job.ImageSize,
Multiplier: 1,
MaxZoom: maxZoom,
MinZoom: job.MinZoom,
MaplibreStyle: assetPath(stylesPrefix, "standard.json"),
Name: worldName,
WorldSize: job.WorldSize,
ImageSize: job.ImageSize,
Multiplier: 1,
MaxZoom: maxZoom,
MinZoom: job.MinZoom,
Maplibre: job.HasMaplibre,
}
if err := writeJSON(filepath.Join(job.OutputDir, "map.json"), doc); err != nil {
return fmt.Errorf("write map.json: %w", err)
Expand Down
2 changes: 2 additions & 0 deletions internal/maptool/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type Job struct {
HasHillshade bool `json:"-"`
HasColorRelief bool `json:"-"`
GradMehMeta *GradMehMeta `json:"-"` // original grad_meh metadata
HasMaplibre bool `json:"-"` // set by generate_styles stage

// SubDirs enables organized output layout (tiles/, styles/ subdirectories).
// When true, PMTiles go to OutputDir/tiles/ and styles go to OutputDir/styles/.
Expand All @@ -77,6 +78,7 @@ func (j *Job) StylesOutputDir() string {
return j.OutputDir
}


// JobInfo is a read-only snapshot of a Job, safe for concurrent access and serialization.
type JobInfo struct {
ID string `json:"id"`
Expand Down
2 changes: 1 addition & 1 deletion internal/maptool/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func ScanMaps(mapsDir string) ([]MapInfo, error) {

hasSatellite := fileExistsIn(worldDir, "tiles", "satellite.pmtiles")
hasFeatures := fileExistsIn(worldDir, "tiles", "features.pmtiles")
hasStyle := fileExistsIn(worldDir, "styles", "standard.json")
hasStyle := fileExistsIn(worldDir, "styles", "color-relief.json")
hasMapJSON := false

mapJSONPath := filepath.Join(worldDir, "map.json")
Expand Down
4 changes: 2 additions & 2 deletions internal/maptool/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestScanMaps_Complete(t *testing.T) {
require.NoError(t, os.WriteFile(filepath.Join(altisDir, "map.json"), mapData, 0644))
require.NoError(t, os.WriteFile(filepath.Join(altisDir, "tiles", "satellite.pmtiles"), []byte("fake"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(altisDir, "tiles", "features.pmtiles"), []byte("fake"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(altisDir, "styles", "standard.json"), []byte("{}"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(altisDir, "styles", "color-relief.json"), []byte("{}"), 0644))

maps, err := ScanMaps(dir)
require.NoError(t, err)
Expand Down Expand Up @@ -68,7 +68,7 @@ func TestScanMaps_RootFallback(t *testing.T) {
require.NoError(t, os.WriteFile(filepath.Join(worldDir, "map.json"), []byte(`{"worldSize":5120}`), 0644))
require.NoError(t, os.WriteFile(filepath.Join(worldDir, "satellite.pmtiles"), []byte("fake"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(worldDir, "features.pmtiles"), []byte("fake"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(worldDir, "standard.json"), []byte("{}"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(worldDir, "color-relief.json"), []byte("{}"), 0644))

maps, err := ScanMaps(dir)
require.NoError(t, err)
Expand Down
59 changes: 51 additions & 8 deletions internal/maptool/styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,10 @@ func makeVegetationStyle(name, iconImage string, iconSize float64) LayerStyle {
type StyleVariant string

const (
StyleStandard StyleVariant = "standard"
StyleSatellite StyleVariant = "satellite"
StyleHybrid StyleVariant = "hybrid"
StyleColorRelief StyleVariant = "color-relief"
StyleTopo StyleVariant = "topo"
StyleSatellite StyleVariant = "satellite"
StyleHybrid StyleVariant = "hybrid"
)

// StyleConfig holds the parameters for generating a style document.
Expand All @@ -509,16 +510,23 @@ const (
func GenerateStyleDocument(cfg StyleConfig, variant StyleVariant) map[string]interface{} {
sources := buildSources(cfg)

bgColor := "#000000"
if variant == StyleTopo {
bgColor = "#DFDFDF"
}

var layers []interface{}
layers = append(layers, map[string]interface{}{
"id": "background",
"type": "background",
"paint": map[string]interface{}{"background-color": "#000000"},
"paint": map[string]interface{}{"background-color": bgColor},
})

switch variant {
case StyleStandard:
layers = append(layers, buildStandardLayers(cfg)...)
case StyleColorRelief:
layers = append(layers, buildColorReliefLayers(cfg)...)
case StyleTopo:
layers = append(layers, buildTopoLayers(cfg)...)
case StyleSatellite:
layers = append(layers, buildSatelliteLayers(cfg)...)
case StyleHybrid:
Expand Down Expand Up @@ -580,9 +588,9 @@ func buildSources(cfg StyleConfig) map[string]interface{} {
return sources
}

// --- Standard style layers ---
// --- Color Relief style layers ---

func buildStandardLayers(cfg StyleConfig) []interface{} {
func buildColorReliefLayers(cfg StyleConfig) []interface{} {
var layers []interface{}

// Color relief
Expand Down Expand Up @@ -614,6 +622,41 @@ func buildStandardLayers(cfg StyleConfig) []interface{} {
return layers
}

// --- Topo style layers (color relief hidden) ---

func buildTopoLayers(cfg StyleConfig) []interface{} {
var layers []interface{}

// Color relief present but hidden
if cfg.HasColorRelief {
layers = append(layers, map[string]interface{}{
"id": "color-relief", "type": "raster", "source": "color-relief",
"layout": map[string]interface{}{"visibility": "none"},
})
}

// Hillshade (raster) at 50% opacity
if cfg.HasHillshade {
layers = append(layers, map[string]interface{}{
"id": "hillshade-raster", "type": "raster", "source": "hillshade",
"paint": map[string]interface{}{"raster-opacity": 0.5},
})
}

// Satellite (hidden by default)
if cfg.HasSatellite {
layers = append(layers, map[string]interface{}{
"id": "satellite", "type": "raster", "source": "satellite",
"layout": map[string]interface{}{"visibility": "none"},
})
}

// Vector feature layers (same as color relief)
layers = append(layers, buildVectorFeatureLayers(cfg.VectorLayers, layerVisStandard)...)

return layers
}

// --- Satellite style layers ---

func buildSatelliteLayers(cfg StyleConfig) []interface{} {
Expand Down
10 changes: 5 additions & 5 deletions internal/maptool/styles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,10 @@ func TestGenerateStyleDocument_Structure(t *testing.T) {
HasSatellite: true,
}

doc := GenerateStyleDocument(cfg, StyleStandard)
doc := GenerateStyleDocument(cfg, StyleColorRelief)

assert.Equal(t, 8, doc["version"])
assert.Equal(t, "altis-standard", doc["name"])
assert.Equal(t, "altis-color-relief", doc["name"])
assert.NotNil(t, doc["sources"])
assert.NotNil(t, doc["layers"])
assert.NotEmpty(t, doc["sprite"])
Expand All @@ -238,7 +238,7 @@ func TestGenerateStyleDocument_Variants(t *testing.T) {
HasHillshade: true,
}

for _, variant := range []StyleVariant{StyleStandard, StyleSatellite, StyleHybrid} {
for _, variant := range []StyleVariant{StyleColorRelief, StyleTopo, StyleSatellite, StyleHybrid} {
t.Run(string(variant), func(t *testing.T) {
doc := GenerateStyleDocument(cfg, variant)
assert.Equal(t, "stratis-"+string(variant), doc["name"])
Expand All @@ -262,7 +262,7 @@ func TestGenerateStyleDocument_Sources(t *testing.T) {
HasColorRelief: true,
}

doc := GenerateStyleDocument(cfg, StyleStandard)
doc := GenerateStyleDocument(cfg, StyleColorRelief)
sources := doc["sources"].(map[string]interface{})

assert.Contains(t, sources, "features")
Expand All @@ -279,7 +279,7 @@ func TestGenerateStyleDocument_NoOptionalSources(t *testing.T) {
VectorLayers: []string{"sea"},
}

doc := GenerateStyleDocument(cfg, StyleStandard)
doc := GenerateStyleDocument(cfg, StyleColorRelief)
sources := doc["sources"].(map[string]interface{})

assert.Contains(t, sources, "features")
Expand Down
Loading
Loading