Skip to content

Highway Shields

Country-aware route markers driven by the network attribute on transportation and transportation_name.

Why this matters

A road tagged M1 looks completely different in:

  • US (M1 = motorway in NY) → black-on-yellow oval
  • UK (M1 = London-Leeds motorway) → blue-on-white pill
  • Germany (no M1) → not applicable

Our Lua tile pipeline classifies every road's network from its OSM tags, so styles can render the right shield without parsing ref strings on the client.

Sprite

Required sprite — hosted at /sprites/shields?key=…. Each icon is named after its network:

Sprite nameNetwork valueVisual
shield-us-interstateus-interstateRed/blue/white pentagon
shield-us-highwayus-highwayBlack-on-white shield
shield-us-stateus-stateBlack-on-white circle/square
shield-de-bundesautobahnde-bundesautobahnBlue rectangle "A"
shield-de-bundesstrassede-bundesstrasseYellow rectangle "B"
shield-gb-motorwaygb-motorwayBlue pill "M"
shield-gb-trunkgb-trunkGreen pill "A"
shield-fr-autoroutefr-autorouteRed pill "A"
shield-fr-route-nationalefr-route-nationaleRed rectangle "N"
shield-e-roade-roadGreen rectangle "E"
shield-asian-highwayasian-highwayBlue rectangle "AH"
shield-roadroad (fallback)Generic white pill

Style layer

json
{
  "id": "highway-shields",
  "type": "symbol",
  "source": "mfd",
  "source-layer": "transportation_name",
  "filter": ["all",
    ["has", "ref"],
    ["match", ["get", "class"], ["motorway", "trunk", "primary"], true, false]
  ],
  "layout": {
    "icon-image": [
      "concat", "shield-",
      ["coalesce", ["get", "network"], "road"]
    ],
    "icon-size": ["interpolate", ["linear"], ["zoom"], 8, 0.5, 16, 1.0],
    "icon-text-fit": "both",
    "icon-text-fit-padding": [1, 4, 1, 4],
    "text-field": ["get", "ref"],
    "text-font": ["Noto Sans Bold"],
    "text-size": ["interpolate", ["linear"], ["zoom"], 8, 9, 16, 13],
    "symbol-placement": "line",
    "symbol-spacing": 200
  },
  "paint": {
    "text-color": "#000",
    "text-halo-color": "#fff",
    "text-halo-width": 0
  }
}

Fallback chain

Some refs span countries (e.g. E-roads E40, AH-roads AH1). The Lua classifier picks the most specific country network when present, falls back to international networks (e-road, asian-highway), then to road.

If network is null, sprite name shield-road is used — always exists.

Custom sprite (your own brand)

Replace the hosted sprite with yours:

json
"sprite": "https://your-cdn.example.com/my-shields/v1"

Names must match the network values exactly: shield-us-interstate.png, etc.

Generate from SVG with spreet:

bash
spreet ./shields-svg/ ./output/my-shields
# produces my-shields.png + my-shields.json

Status

Shield rendering ships in style v1.1 (estimated 2026-05). Until then the network attribute is in tiles but the hosted style renders simple text refs.