Skip to content

Use addProtocol to transform feature properties

Replicates the MapLibre example Use addProtocol to transform feature properties. The YAML declares a GeoJSON source whose URL starts reverse://, and the layers that label it. The protocol is page JS: registered with addProtocol from @maplibre-yaml/core/maplibre, it fetches the real URL, writes every NAME and ABBREV backwards, and hands the result back as the response. The document never knows.

Country names, reversed between the server and the map
use-addprotocol-to-transform-feature-properties.yaml
# yaml-language-server: $schema=https://docs.maplibre-yaml.org/schema/latest/map.schema.json
# Gallery twin of https://maplibre.org/maplibre-gl-js/docs/examples/use-addprotocol-to-transform-feature-properties/
# The YAML half: a GeoJSON source whose URL goes through a `reverse://`
# protocol, and the layers that label it. The protocol — fetch the real
# URL, rewrite every NAME and ABBREV backwards, hand the data back — is
# page JS registered on <ml-map>'s maplibre module; see
# use-addprotocol-to-transform-feature-properties.js. The YAML never knows.
#
# NOTE for the live sweep: renders only with the page's protocol
# registration; scripts/verify-docs-gallery.mjs drives it through the
# hatch harness, which runs that JS.
type: map
id: use-addprotocol-to-transform-feature-properties
config:
center: [8, 47]
zoom: 5
mapStyle: "https://demotiles.maplibre.org/style.json"
sources:
countries:
type: geojson
data: "reverse:///gallery-assets/alpine-countries.geojson"
layers:
- id: country-points
type: circle
source: countries
paint:
circle-radius: 5
circle-color: "#e63946"
- id: country-names
type: symbol
source: countries
layout:
text-field: ["format", ["get", "NAME"], {}, "\n", {}, ["get", "ABBREV"], { font-scale: 0.75 }]
text-font: ["Noto Sans Regular"]
text-offset: [0, 0.4]
text-anchor: top
paint:
text-color: "#1d3557"
text-halo-color: "#ffffff"
text-halo-width: 1.5
use-addprotocol-to-transform-feature-properties.js
// The JavaScript half: addProtocol, imported from @maplibre-yaml/core/maplibre
// so it registers on the very module <ml-map> renders with. Any source URL
// that starts `reverse://` now comes through this function: fetch the real
// URL, rewrite the properties, return the result as the response.
// (Upstream does the same to vector tiles, decoding them with
// @mapbox/vector-tile and re-encoding with vt-pbf; GeoJSON needs neither.)
import { addProtocol } from "@maplibre-yaml/core/maplibre";
const reverse = (text) => [...text].reverse().join("");
addProtocol("reverse", async (request) => {
const url = request.url.slice("reverse://".length);
const geojson = await (await fetch(url)).json();
for (const { properties } of geojson.features) {
for (const key of ["NAME", "ABBREV"]) {
if (typeof properties?.[key] === "string") properties[key] = reverse(properties[key]);
}
}
return { data: geojson };
});
  • The import path matters: @maplibre-yaml/core/maplibre is the exact module <ml-map> renders with, so the protocol lands where the document’s requests go. See PMTiles source and protocol.
  • Upstream transforms vector tiles, decoding them with @mapbox/vector-tile and re-encoding with vt-pbf; the protocol code is identical, only the payload differs.