Map Styles Overview
One canonical styles endpoint that returns a renderer-correct style document for MapLibre / Mapbox, Leaflet, or Google Maps + deck.gl. Pick your target, list the layers you want, and mount.
Styles URL builder
Pick a renderer target and one or more layers — the URL below updates live. CDL years are expressed one per spec (e.g. cdl:2024); add multiple to stack years. Swap YOUR_API_KEY for your key when you paste this into your code.
Target
MapLibre and Mapbox return the identical MapLibre Style Spec v8 document — pick whichever name fits your codebase.
Layers
Request URL
https://api.landmapmagic.com/v1/styles?target=maplibre&layers=clu&key=YOUR_API_KEY
The key param is a placeholder — the live URL needs your real API key to return a style.
https://api.landmapmagic.com/v1/stylesWhy one endpoint
Every mapping library wants the same data shaped a little differently. Rather than ship a different style file per layer per target, the API answers a single question: "Given these layers and this target, hand me a ready-to-mount config." Each target gets a renderer-native document so you never have to translate paint properties yourself.
- Duplicate-label rendering (multi-polygon features and tile-boundary repeats) is solved server-side via dedicated
*_labelssource layers and a canonicallmm_label_id. - Zoom ranges, attribution, and tile URLs come from the API's authoritative product catalog — they never drift between client and server.
- The landmapmagic npm package consumes this endpoint, so style updates roll out without npm releases.
Parameters
Parameters
| Name | Type | Description |
|---|---|---|
| key* | string | Your API key (origin-restricted). |
| target | string | Renderer target. One of: maplibre (default — also serves Mapbox GL JS), leaflet, google. |
| layers* | string (CSV) | Comma-separated layer ids. Supported: clu, states, counties, townships, sections, parcels, plss, ssurgo, cdl. CDL years are expressed one per spec — cdl:YYYY (e.g. cdl:2024). Repeat to request multiple years (cdl:2024,cdl:2023). Bare `cdl` resolves to the default year. |
Targets at a glance
What each target returns
| Attribute | Type | Description |
|---|---|---|
| maplibre | MapLibre Style Spec v8 | Full style document with sources, layers, glyphs, and metadata. Default target. |
| mapbox | MapLibre Style Spec v8 (alias) | Identical payload to maplibre. Mapbox GL JS consumes a MapLibre Style Spec v8 doc as-is — the alias exists so you can write `target=mapbox` in your code without anyone wondering whether they should switch. |
| leaflet | { basemap?, sources, layers } | Array of declarative Leaflet descriptors (tileLayer for raster, vectorGrid for vector) with URLs, options, and label metadata. The landmapmagic/leaflet adapter mounts these directly. |
| { tileLayers, vectorOverlays, labels } | deck.gl-friendly envelope with MVT / raster tile URLs and accessor descriptors (static, by-zoom, by-property). Mounted via deck.gl GoogleMapsOverlay using MVTLayer / TextLayer / TileLayer + BitmapLayer. |
Label deduplication
Two distinct "duplicate label" problems are handled server-side so you don't have to think about either:
- Multi-polygon-per-feature — one logical feature (state, parcel, CLU) often consists of many polygons; the dedicated
*_labelssource layers contain a single representative point per feature so labels don't repeat per polygon. - Tile-boundary repeats — when a feature spans multiple tiles, every label feature carries
lmm_label_id. MapLibre collision sorts on it; Leaflet and Google adapters dedupe in JS by id.
Code Examples
MapLibre — fetch + apply
const response = await fetch(
"https://api.landmapmagic.com/v1/styles?" +
new URLSearchParams({
key: "YOUR_API_KEY",
target: "maplibre",
layers: "clu,counties",
})
);
const style = await response.json();
map.setStyle(style);curl "https://api.landmapmagic.com/v1/styles?key=YOUR_API_KEY&target=maplibre&layers=clu,counties"import requests
response = requests.get(
"https://api.landmapmagic.com/v1/styles",
params={
"key": "YOUR_API_KEY",
"target": "maplibre",
"layers": "clu,counties",
},
)
style = response.json()CDL with multiple years
// One layer per year — repeat cdl:YYYY in the layers list
const response = await fetch(
"https://api.landmapmagic.com/v1/styles?" +
new URLSearchParams({
key: "YOUR_API_KEY",
target: "maplibre",
layers: "clu,cdl:2024,cdl:2023",
})
);
const style = await response.json();Leaflet — declarative descriptors
const response = await fetch(
"https://api.landmapmagic.com/v1/styles?" +
new URLSearchParams({
key: "YOUR_API_KEY",
target: "leaflet",
layers: "clu",
})
);
const { layers } = await response.json();
// Each entry has type: 'tileLayer' | 'vectorGrid' and an
// options object with the URL template + style rules.
console.log(layers);Google + deck.gl
const response = await fetch(
"https://api.landmapmagic.com/v1/styles?" +
new URLSearchParams({
key: "YOUR_API_KEY",
target: "google",
layers: "clu",
})
);
const { tileLayers, vectorOverlays } = await response.json();
// Pass directly to MVTLayer / TileLayer through GoogleMapsOverlay.