Skip to main content

Extending Massif

The CartoCSS project is meant to be a base. A child project extends it and restates only what it changes, so a new Massif release is picked up by replacing the base files and leaving yours alone. Both CartoCSS flavours (plain and icon font) take a child; the compiled ones do not, since they carry no CartoCSS any more — compile your child instead (massif-style css2xml mine.json mine.xml, style CLI).

A child is a <name>.json beside project.json, loaded like a variant (name: 'mine'):

{
"extends": "./project.json",
"styles": ["mine-colors.mss", "variables.mss", "style.mss", "mine-rules.mss"],
"styleparameters": {
"variant": { "default": "streets", "values": { "streets": "streets", "outdoor": "outdoor", "topo": "topo", "hybrid": "hybrid", "eink": "eink" } },
"poiStyle": { "default": "plain", "values": { "badge": "badge", "plain": "plain" } },
"track_min_zoom": 13
}
}

Three things it can do:

  • Colours and fonts — a stylesheet listed before variables.mss redeclares a variable: @motorway, @water, @wood, @font_medium... The compiler keeps the first declaration of a variable, so yours wins. The names are listed in variables.mss.
  • Rules — a stylesheet listed after style.mss adds rules. A new attachment (#landcover[class = 'wood']::mine_pattern { … }) draws with its layer, over the base, without touching it.
  • Defaults — styleparameters merge key by key: set a parameter's default, or declare a new one for your own rules. styles does not merge; it is restated whole.

A layer is replaced by moving the base one out of reach through its parameter and drawing your own: track_min_zoom: 24 hides Massif's tracks, a rule after style.mss draws them your way. POIs are one template, %poi, drawn into one attachment, ::poi, so a child widens a class — draws it where the base's rank ladder does not yet — or narrows one, with a rule of its own:

#poi[zoom >= 15][class = 'bakery']::poi {
@extend %poi;
shield-placement-priority: (30000000 + (((([param::poi-boost-[subclass]]) ?? (0)) != 0) ? (([param::poi-boost-[subclass]]) ?? (0)) : (([param::poi-boost-[class]]) ?? (0))));
}
#poi[zoom < 17][class = 'pharmacy']::poi { display: none; }

The term after 30000000 is the one every base POI rule adds, so an app's poi-boost-<class> still reaches a rule that states its own priority. To only re-rank a class, set poi-boost-<class> instead of writing a rule (style README).

The bus and tram stops' zooms are project constants, moved with no rule at all:

"constants": { "poi_bus_minzoom": 15, "poi_bus_label_minzoom": 16, "poi_tram_minzoom": 16,
"poi_tram_label_minzoom": 16, "poi_pt_shelter_minzoom": 15 }

So are the rank ladder's: poi_rank10_minzoom (14), poi_rank30_minzoom (15), poi_rank30_label_minzoom (16), poi_rank70_minzoom (16), poi_rank_all_minzoom (17).

How the template, the attachment and display compile: CartoCSS templates.

The OpenStreetMap example​

osm.json ships in both CartoCSS flavours: Massif re-skinned with OpenStreetMap Carto's colours, as Alpimaps' OSM style has them. Load it as name: 'osm'. Its three files:

  • osm-colors.mss (before variables.mss) — the palette redeclared: OSM Carto's road colours, some ramped over zoom as OSM Carto ramps them (a variable may hold an expression: @primary: linear([view::zoom], (11, #f3ba5c), (12, #fcd6a4));), its water, woods, landuse and buildings.
  • osm-rules.mss (after style.mss) — what the palette cannot say: tracks as a brown line under white dashes by tracktype, OSM Carto's textures over woods, scrub, wetland and rock, and the POIs above (bakeries early, pharmacies late).
  • osm.json — the defaults: POIs as bare glyphs (poiStyle: plain), Massif's tracks out of reach (track_min_zoom: 24), roads outlined and widened at low zoom as OSM Carto does (road_osm_low: 1), tunnels plain until z13 (tunnel_min_zoom: 13), bus stops from z15, named from z16, and public-transport shelters from z15 (constants).

custom.json is the smaller example: two colours, one rule, a new parameter.

Two limits. A variable names a value: two palette entries with the same colour share one name, and redeclaring it changes both. And a child changes the CartoCSS only.

The MapLibre side​

MapLibre has no child styles. The MapLibre variants are generated by styles/massif/build.py from a Python palette and layer modules: a MapLibre variant of your own is a fork of that folder — edit palette.py, run python3 build.py — or a JSON patch over a published style.