The three surfaces
Which style you write depends on what the layer is and how it's delivered:
| Your layer | Delivered as | Flag | Style format |
|---|---|---|---|
Raster (--cog) | WMS picture / tiles | --style | A small JSON: RGB or a colour ramp |
Vector (--vector) | WMS picture (drawn on the server) | --vec-style | SLD, or a simple marker/label JSON |
Vector (--vector) | Vector tiles (drawn in the browser) | --mvt-style | A MapLibre GL style |
A vector layer served both ways (like the land-cover demo) carries two styles:
one --vec-style
for the picture and one --mvt-style
for the tiles. Keep their colours in sync (see the last section).
Raster: RGB image
For an ordinary colour photo (an orthophoto, a true-colour satellite image): map three bands to red, green, blue.
{
"mode": "rgb",
"bands": [1, 2, 3], // which bands become R, G, B
"alpha": "mask" // transparent where the image has no data
}
Use it with --style rgb.json.
Raster: pseudocolor ramp
For a single-value raster (elevation, temperature, NDVI): map values to colours with
a list of stops. Each stop is
[value, red, green, blue, alpha];
TerraServe blends smoothly between them. This NDVI ramp runs brown -> yellow -> green:
{
"mode": "pseudocolor",
"band": 1,
"nodata_transparent": true,
"stops": [
[-0.2, 165, 0, 38, 255], // bare / water
[ 0.0, 215, 48, 39, 255],
[ 0.2, 254, 224, 139, 255],
[ 0.4, 166, 217, 106, 255],
[ 0.6, 26, 152, 80, 255],
[ 0.8, 0, 104, 55, 255] // lush vegetation
]
}
Use it with --style ndvi.json.
This is what colours the live-NDVI demo, and because it's just data, you can retune
the ramp without touching the raster.
Vector picture: SLD (the standard)
SLD (Styled Layer Descriptor) is the OGC standard for styling maps, and the right
choice for thematic data: colour-by-class, with per-class rules. TerraServe
reads SLD 1.0. Each <Rule>
is a filter (which features it matches) plus a symbolizer (how to draw them). One
rule from the land-cover style:
<Rule>
<Name>1.1.1.1</Name>
<Title>Continuous urban fabric</Title>
<ogc:Filter><ogc:PropertyIsEqualTo>
<ogc:PropertyName>COS23_n4_C</ogc:PropertyName>
<ogc:Literal>1.1.1.1</ogc:Literal>
</ogc:PropertyIsEqualTo></ogc:Filter>
<PolygonSymbolizer><Fill>
<CssParameter name="fill">#e6004d</CssParameter>
</Fill></PolygonSymbolizer>
</Rule>
Repeat one rule per class. Supported symbolizers:
PolygonSymbolizer
(fill + stroke, incl. road casing),
LineSymbolizer,
PointSymbolizer
(marker), and
TextSymbolizer
(labels, with priority and placement). Use it with
--vec-style cos2023.sld.
Vector picture: marker & label JSON
When you just need points with labels (airports, place names) and don't want the ceremony of XML, use the small marker/label JSON instead:
{
"mode": "vector",
"point": {
"radius": 3.0,
"fill": [30, 30, 30, 255],
"stroke": [255, 255, 255, 255],
"stroke_width": 1.0
},
"text": {
"label": "name", // which attribute to show
"priority": "scalerank", // which labels win when they collide
"size": 16.0,
"color": [20, 20, 20, 255],
"halo": { "color": [255, 255, 255, 230], "radius": 2.0 },
"offset": 4.0
}
}
Use it with --vec-style airports.vec.json.
Colours are [R, G, B, A], 0 to 255.
Vector tiles: MapLibre GL style
Vector tiles are drawn in the browser, so they're styled with a MapLibre
GL style, the same format Mapbox / MapLibre maps use. TerraServe serves it at
/mvt/{layer}/style.json
and fills in the boilerplate (version, sources) for you; you provide the
layers.
Colour-by-class uses a match expression on an attribute:
{
"layers": [
{
"id": "cos-fill",
"type": "fill",
"filter": ["==", "$type", "Polygon"],
"paint": {
"fill-color": [
"match", ["get", "COS23_n4_C"],
"1.1.1.1", "#e6004d",
"2.1.1.1", "#ffffa8",
"3.1.1.1", "#4dff00",
/* … one pair per class … */
"#cccccc" // fallback colour
]
}
}
],
"metadata": { "legend": [ /* see below */ ] }
}
Use it with --mvt-style cos2023.mvt-style.json.
A client (or QGIS' "Vector Tiles -> Style URL") loads it straight from
/mvt/{layer}/style.json.
The legend
Add a metadata.legend
array to the MapLibre style and the built-in X-ray viewer (and any legend-aware
client) will show a swatch list. Each entry is a colour and a label:
"metadata": {
"legend": [
{ "color": "#e6004d", "label": "Artificial surfaces" },
{ "color": "#ffffa8", "label": "Agriculture" },
{ "color": "#4dff00", "label": "Forest" }
]
}
Example swatches:
Artificial
Agriculture
Forest.
If you omit the legend, TerraServe falls back to the colours it can read from the
style's match expression.
Keeping styles in sync
When a layer is served both as a picture and as tiles, it has an SLD and a MapLibre style: two files describing the same colours. If they drift, the WMS view and the tile view of the same map disagree.
<CssParameter name="fill">
and the MapLibre match
pairs. The land-cover demo does exactly this. Its two files are kept in lockstep, class for class.