Google Maps Integration

Bring LandMapMagic vector and raster layers into Google Maps without writing your own deck.gl plumbing. Use the one-line landmapmagic/google adapter or fetch /v1/styles?target=google directly.

Prerequisites

  • LandMap API key with access to the layers you want (e.g. clu, cdl).
  • Google Maps JavaScript API key + a vector-capable Map ID.
  • deck.gl + @deck.gl/google-maps + @deck.gl/geo-layers as peer dependencies.
bash
npm install landmapmagic deck.gl @deck.gl/google-maps @deck.gl/geo-layers @googlemaps/js-api-loader

One-line install (Recommended)

The landmapmagic/google adapter fetches a deck.gl-friendly style document and wires up MVTLayer / TileLayer / TextLayer for you, including label deduplication via lmm_label_id.

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

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

const map = new google.maps.Map(document.getElementById('map')!, {
  center: { lat: 41.878, lng: -93.097 },
  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'],
});

// Later: handle.unmount();

Manual deck.gl integration

Prefer to keep deck.gl plumbing in your own hands? Fetch a target=google style and turn the descriptors into deck.gl layers yourself.

typescript
import { Loader } from '@googlemaps/js-api-loader';
import { GoogleMapsOverlay } from '@deck.gl/google-maps';
import { MVTLayer, TileLayer } from '@deck.gl/geo-layers';
import { BitmapLayer } from '@deck.gl/layers';

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

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

const response = await fetch(
  'https://api.landmapmagic.com/v1/styles?' +
    new URLSearchParams({
      key: 'YOUR_API_KEY',
      target: 'google',
      layers: 'clu',
    })
);
const { vectorOverlays } = await response.json();

const cluOverlay = vectorOverlays.find((o) => o.id === 'clu');
const polygonSub = cluOverlay.subLayers.find((s) => s.kind === 'polygon');

const overlay = new GoogleMapsOverlay({
  layers: [
    new MVTLayer({
      id: 'clu-outline',
      data: cluOverlay.tileUrl,
      minZoom: cluOverlay.minZoom,
      maxZoom: cluOverlay.maxZoom,
      lineWidthMinPixels: 1,
      // For full accessor handling (static / by-zoom / by-property),
      // use mountGoogleLandMap from landmapmagic/google instead.
      getLineColor: polygonSub.accessors.getLineColor.value,
    }),
  ],
});
overlay.setMap(map);

Customizing colors and accessors

The styles API has no override query parameters. Mutate the deck.gl-friendly accessor descriptors before you mount, or replace props on the live overlay. See the Customizing Styles guide for full recipes.

typescript
const polygonSub = cluOverlay.subLayers.find((s) => s.kind === 'polygon');
polygonSub.accessors.getLineColor = { kind: 'static', value: [255, 107, 53, 255] };
polygonSub.accessors.getLineWidth = { kind: 'static', value: 3 };

CDL raster overlays

CDL responses come back as tileLayers entries (raster PNGs over an XYZ schema). Repeat cdl:YYYY to layer multiple years.

typescript
const response = await fetch(
  'https://api.landmapmagic.com/v1/styles?' +
    new URLSearchParams({
      key: 'YOUR_API_KEY',
      target: 'google',
      layers: 'clu,cdl:2024,cdl:2023',
    })
);
const { tileLayers, vectorOverlays } = await response.json();
// Use mountGoogleLandMap to mount everything, or handle each yourself.

Performance & Quotas

  • MVT and PNG tiles are edge-cached via Cloudflare — reuse URLs to maximize cache hits.
  • Throttle redraws to idle/tilesloaded events to avoid starving the main thread.
  • Restrict client-side API keys by allowed origin in the dashboard.
  • fetchLandStyle caches per (target, layers) for the session, so repeated calls inside one page load are free.

Troubleshooting

  • 401/403 responses — verify the key parameter and allowed origin configuration.
  • Empty overlays — confirm zoom >= the layer's minzoom (CLU starts at Z11, parcels at Z14).
  • Visual artifacts — make sure your Google Map is using a vector Map ID; raster Map IDs don't support deck.gl overlays.
Need MapLibre / Mapbox integration too? See the MapLibre integration guide — same fetch pattern, different target.