One-line install

Add LandMapMagic layers to MapLibre, Mapbox, Leaflet, or Google Maps with a single import. The landmapmagic npm package fetches a target-shaped style from /v1/styles and mounts it for you.

Install

npm
npm install landmapmagic
pnpm
pnpm add landmapmagic
yarn
yarn add landmapmagic
Subpath adapters depend on their renderer being installed: leaflet for Leaflet, @deck.gl/core + @deck.gl/google-maps + @deck.gl/geo-layersfor Google + deck.gl. They're peer dependencies, so you only pull in what you actually use.

MapLibre / Mapbox (React)

The React component fetches the style internally on mount and handles hover / click feature state for you. MapLibre GL JS and Mapbox GL JS both consume the same MapLibre Style Spec v8 doc, so pick whichever renderer fits your stack — the underlying style is identical.

MapLibre
import { LandMap } from 'landmapmagic';

export default function App() {
  return (
    <LandMap
      apiKey="YOUR_API_KEY"
      layers={['clu', 'counties']}
      initialView={{ center: [-93.5, 42.0], zoom: 12 }}
    />
  );
}
Mapbox
import { LandMap } from 'landmapmagic/mapbox';

export default function App() {
  return (
    <LandMap
      apiKey="YOUR_API_KEY"
      mapboxAccessToken="pk...."
      layers={['clu', 'counties']}
      initialView={{ center: [-93.5, 42.0], zoom: 12 }}
    />
  );
}

MapLibre / Mapbox (vanilla)

If you already manage your own map instance, fetch the style and merge it manually. fetchLandStyle caches per-session and returns the renderer-shaped object directly.

JavaScript
import { fetchLandStyle } from 'landmapmagic';

const style = await fetchLandStyle({
  apiKey: 'YOUR_API_KEY',
  target: 'maplibre',
  layers: ['clu', 'counties'],
});

// MapLibre style spec v8 — drop directly into setStyle, or merge sources/layers.
map.setStyle(style);

// Or merge into an existing style:
for (const [id, source] of Object.entries(style.sources)) {
  if (!map.getSource(id)) map.addSource(id, source);
}
for (const layer of style.layers) {
  if (!map.getLayer(layer.id)) map.addLayer(layer);
}

Leaflet

The landmapmagic/leaflet adapter mounts vector layers with L.vectorGrid and rasters with L.tileLayer, and handles label deduplication via lmm_label_id.

JavaScript
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet.vectorgrid';
import { mountLeafletLandMap } from 'landmapmagic/leaflet';

const map = L.map('map').setView([42.0, -93.5], 12);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© OpenStreetMap',
}).addTo(map);

const handle = await mountLeafletLandMap({
  apiKey: 'YOUR_API_KEY',
  map,
  layers: ['clu', 'counties'],
});

// handle.unmount() removes every added layer.

Google Maps + deck.gl

The landmapmagic/google adapter wraps deck.gl's GoogleMapsOverlay with MVTLayer / TileLayer / TextLayer and resolves accessor descriptors (static, by-zoom, by-property) automatically.

JavaScript
import { Loader } from '@googlemaps/js-api-loader';
import { GoogleMapsOverlay } from '@deck.gl/google-maps';
import { mountGoogleLandMap } from 'landmapmagic/google';

const loader = new Loader({ apiKey: 'GMAPS_KEY', version: 'weekly' });
await loader.load();

const map = new google.maps.Map(document.getElementById('map'), {
  center: { lat: 42.0, lng: -93.5 },
  zoom: 12,
  mapId: 'YOUR_VECTOR_MAP_ID',
});

const overlay = new GoogleMapsOverlay({});
overlay.setMap(map);

const handle = await mountGoogleLandMap({
  apiKey: 'YOUR_API_KEY',
  map,
  overlay,
  layers: ['clu', 'counties'],
});

// handle.unmount() clears the deck.gl layers.

Working with multiple CDL years

Each CDL year is its own layer entry. Repeat cdl:YYYY in your layers array; the API returns a unique source/layer per year so you can toggle them independently.

Layers array
await fetchLandStyle({
  apiKey: 'YOUR_API_KEY',
  target: 'maplibre',
  layers: ['clu', 'cdl:2024', 'cdl:2023', 'cdl:2022'],
});
Need to override colors, hide labels, or re-skin a layer after mounting? See the Customizing Styles guide for renderer-specific recipes.