Layers and Targets¶
Layer configuration files define layer definitions (reusable data source + processing config) and build targets (what to produce). They reference source IDs from source config files.
Concepts¶
Layers (definitions)¶
Layer definitions describe what data to use and how to process it. They are reusable and have no output file or exporter — they are purely definitions.
Targets (build instructions)¶
Build targets describe what to produce. Each target specifies an output file, an exporter, and an ordered list of layer entries. A target can reference defined layers (by ref) or define layers inline.
Single-layer targets are the simplest case — one layer entry producing one file. Composite targets combine multiple layer entries, blended bottom-to-top using alpha compositing (painter's algorithm).
File structure¶
A typical layer config file has three sections:
includes:
- ../sources/swisstopo.yaml
# Default bounding box for all layers/targets in this file
bounds:
west: 5.96
east: 10.49
south: 45.82
north: 47.81
# Layer definitions (reusable, no output)
layers:
my_basemap:
name: "My Basemap"
format: wmts
source:
ref: my_wmts
layer: ch.swisstopo.pixelkarte-farbe
zoom_levels: [8, 9, 11, 12, 13, 14, 15, 16]
my_overlay:
name: "My Overlay"
format: wmts
source:
ref: my_wmts
layer: ch.swisstopo.skiroutes
extension: png
zoom_levels: [11, 12, 13, 14, 15, 16]
# Build targets (what to produce, with output files)
targets:
my_map:
name: "My Map"
output: my_map.img
zoom_levels: [8, 9, 11, 12, 13, 14, 15, 16]
layers:
- ref: my_basemap
- ref: my_overlay
opacity: 0.6
zoom_levels: [13, 14, 15, 16]
Includes¶
The includes directive loads other config files (typically source definitions). Paths are relative to the current file. Includes are processed depth-first with last-file-wins merge semantics.
File-level bounds¶
A top-level bounds key sets default bounds for all layers and targets in the file. Individual layers and targets can override this.
There are two formats for bounds:
Anonymous bounds (file-level default, backward compatible):
Named bounds (reusable, slug-referenced):
bounds:
switzerland:
west: 5.96
east: 10.49
south: 45.82
north: 47.81
bern:
west: 7.31
east: 7.57
south: 46.88
north: 47.06
The loader auto-detects the format: if all keys are in {west, east, south, north}, it's anonymous; otherwise it's named bounds.
Layers and targets can reference named bounds by slug:
layers:
my_layer:
name: "My Layer"
source: my_source
zoom_levels: [10, 12]
bounds: switzerland # references the named bounds above
Or use inline coordinates:
targets:
my_target:
output: out.img
layers: [...]
bounds: # inline coordinates
west: 7.0
east: 8.0
south: 46.5
north: 47.0
Named bounds are merged across includes with last-file-wins semantics.
Layer definitions¶
Each entry under layers: is a named, reusable definition:
layers:
ch_basemap:
name: "Switzerland Basemap"
description: "Swisstopo national map"
format: wmts
source:
ref: swisstopo_wmts
layer: ch.swisstopo.pixelkarte-farbe
zoom_levels: [8, 9, 11, 12, 13, 14, 15, 16]
Layer fields¶
| Field | Required | Description |
|---|---|---|
name |
yes | Display name for the layer |
description |
no | Layer description |
format |
yes | Data format: geotiff, gpkg, or wmts — selects the processing provider |
source |
yes | Source ID (string) or dict with ref + args (see Source reference) |
source_args |
no | Template variable overrides (merged with source defaults) |
zoom_levels |
yes | List of zoom levels to include |
bounds |
no | Geographic bounds: inline (west, east, south, north), a slug referencing named bounds, or inherited from file-level if omitted |
rules |
no | Inline style rules for vector/rasterized layers |
style |
no | Path to QML style file for vector/rasterized layers |
garmin_types |
no | Garmin type mapping for vector features |
Source reference¶
The source field can be a string (source ID) or a dict with a ref key plus variable overrides:
# String form
source: my_wmts
# Dict form (with variable overrides)
source:
ref: my_wmts
layer: ch.swisstopo.pixelkarte-farbe
extension: png
When using the dict form, all keys except ref become source_args — these override source defaults for template variable resolution.
Format field¶
The format field determines how the data is processed:
| Format | Source types | Description |
|---|---|---|
wmts |
wmts |
WMTS tile service — tiles downloaded and re-encoded |
geotiff |
stac, path |
GeoTIFF raster data — reprojected, mosaicked, and tiled |
gpkg |
stac, path |
GeoPackage vector data — rasterized using style rules |
Backward compat fields¶
wmts_layer— maps tosource_args.layerextension— maps tosource_args.extension
If both a shorthand field and source_args are provided, source_args takes precedence.
Build targets¶
Each entry under targets: defines what to build:
targets:
my_map:
name: "My Map"
output: my_map.img
zoom_levels: [8, 9, 11, 12, 13, 14, 15, 16]
layers:
- ref: my_basemap
Target fields¶
| Field | Required | Description |
|---|---|---|
output |
yes | Output filename (e.g. my_map.img) |
layers |
yes | Ordered list of layer entries (see Layer entries) |
name |
no | Display name |
description |
no | Target description |
exporter |
no | Export format (default: garmin_img) |
zoom_levels |
no | List of zoom levels — inherited from referenced layers if omitted |
bounds |
no | Geographic bounds: inline, a slug referencing named bounds, or inherited from file-level/referenced layers if omitted |
Zoom levels and bounds inheritance¶
Targets can omit zoom_levels and bounds. When omitted:
- zoom_levels: Resolved from the union of all referenced layer definitions' zoom levels
- bounds: Resolved from the enclosing bounding box of all referenced layer definitions' bounds
This keeps targets DRY — the data source definitions own the zoom/bounds, and the target just says "build them all."
Layer entries¶
Each item in a target's layers: list is either a ref entry or an inline entry:
Ref entry¶
References a top-level layer definition. Optional overrides for zoom_levels and opacity:
Inline entry¶
Defines a layer directly in the target (no top-level layer definition needed):
- name: "Overlay"
format: wmts
source:
ref: my_wmts
layer: ch.swisstopo.skiroutes
extension: png
zoom_levels: [13, 14, 15, 16]
opacity: 0.6
Entry fields¶
| Field | Required | Description |
|---|---|---|
ref |
ref only | ID of a top-level layer definition |
source |
inline only | Source ID or dict (same as layer source) |
format |
inline only | Data format (geotiff, gpkg, wmts) |
name |
no | Display name (inherited from ref layer if omitted) |
zoom_levels |
no | Zoom levels this entry contributes to |
opacity |
no | Uniform float (0.0–1.0, default 1.0) or per-zoom dict |
source_args |
no | Template variable overrides |
asset_filter |
no | Key-value filter for STAC asset selection |
rules |
no | Inline style rules for vector/rasterized layers |
style |
no | Path to QML style file |
garmin_types |
no | Garmin type mapping for vector features |
extension |
no | Backward compat: maps to source_args.extension |
An entry must have either ref or source, but not both.
Opacity¶
Opacity controls how transparent a layer entry appears in composite targets:
- Uniform: a float between 0.0 (fully transparent) and 1.0 (fully opaque)
- Per-zoom: a mapping from zoom level to opacity value
Tile fallback¶
When a layer entry declares a zoom level but a specific tile is unavailable (404 from server), the system automatically falls back to the closest lower zoom level in the entry's zoom_levels list and upscales that tile. If no lower-zoom fallback exists, the entry is skipped for that tile position.
Fallback only applies when the zoom level is declared but the tile is missing. Zoom levels intentionally omitted from zoom_levels are not subject to fallback.
Products¶
A products section defines product catalogs for server-side use (e.g., pricing, download tokens). This section is optional and ignored by the CLI pipeline — it exists for the server to consume.
products:
outdoor-summer:
name: "Outdoor Summer"
price: 25.0
currency: CHF
targets: [ch_outdoor_summer]
token_max_downloads: 10
token_expiry_days: 60
sort_order: 1
Product fields¶
| Field | Required | Description |
|---|---|---|
name |
no | Display name (defaults to the product slug) |
price |
no | Price (default: 0.0) |
currency |
no | Currency code (default: CHF) |
targets |
no | List of target slugs this product includes (validated on load) |
token_max_downloads |
no | Max downloads per token (default: 5) |
token_expiry_days |
no | Token validity in days (default: 30) |
sort_order |
no | Display sort order (default: 0) |
All product target references are validated — a product referencing a nonexistent target slug will raise an error at config load time. Products are merged across includes with last-file-wins semantics.
Complete example¶
includes:
- ../sources/swisstopo.yaml
bounds:
west: 5.96
east: 10.49
south: 45.82
north: 47.81
layers:
basemap:
name: "Switzerland Basemap"
format: wmts
source:
ref: swisstopo_wmts
layer: ch.swisstopo.pixelkarte-farbe
zoom_levels: [8, 9, 11, 12, 13, 14, 15, 16]
hiking:
name: "Hiking Trails"
format: wmts
source:
ref: swisstopo_wmts
layer: ch.swisstopo.swisstlm3d-wanderwege
extension: png
zoom_levels: [11, 12, 13, 14, 15, 16]
skiroutes:
name: "Skiroutes"
format: wmts
source:
ref: swisstopo_wmts
layer: ch.swisstopo-karto.skitouren
extension: png
zoom_levels: [11, 12, 13, 14, 15, 16]
targets:
winter_map:
name: "Switzerland Winter Outdoor"
output: ch_winter.img
zoom_levels: [8, 9, 11, 12, 13, 14, 15, 16]
layers:
- ref: basemap
- ref: hiking
opacity: {13: 0.4, 14: 0.6, 15: 0.7, 16: 0.7}
zoom_levels: [13, 14, 15, 16]
- ref: skiroutes
opacity: {13: 0.5, 14: 0.6, 15: 0.7, 16: 0.7}
zoom_levels: [13, 14, 15, 16]
simple_basemap:
output: ch_basemap.img
layers:
- ref: basemap
Note how simple_basemap omits zoom_levels and bounds — they are inherited from the referenced basemap layer definition.