Google Maps Integration
Bring Common Land Unit (CLU) boundaries into Google Maps without rebuilding your map stack.
Prerequisites
- LandMap API key with access to the
cludataset - Google Maps JavaScript API key and an initialized map
- Optional:
deck.glfor quicker iteration with vector tiles
# For deck.gl approach (recommended)
npm install deck.gl @deck.gl/google-mapsFetch the CLU Style Metadata
The API exposes a MapLibre style document that centralizes colors, labels, and layer IDs. Even though Google Maps cannot ingest the style directly, reusing its values keeps your presentation consistent across platforms.
GET https://api.landmapmagic.com/v1/styles/clu/style.json?key=YOUR_API_KEY
Runtime overrides:
borderColor=#fde047
hoverColor=#ffd700
labelColor=#000000
labels=falseOption A — deck.gl MVTLayer (Recommended)
Deck.gl's MVTLayer loads MVT tiles directly and integrates cleanly with Google Maps via @deck.gl/google-maps.
import { Loader } from '@googlemaps/js-api-loader';
import { GoogleMapsOverlay } from '@deck.gl/google-maps';
import { MVTLayer } from '@deck.gl/geo-layers';
const loader = new Loader({
apiKey: process.env.GMAPS_KEY!,
version: 'weekly'
});
loader.load().then(() => {
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({
layers: [
new MVTLayer({
id: 'clu-outline',
data: 'https://api.landmapmagic.com/v1/tiles/clu/{z}/{x}/{y}.mvt?key=YOUR_API_KEY',
minZoom: 11,
maxZoom: 15,
lineWidthMinPixels: 1,
getLineColor: [253, 224, 71, 255], // #fde047
})
]
});
overlay.setMap(map);
});- Add a second MVTLayer targeting clu_labels to render acreage labels
- Fetch /v1/styles/clu/style.json once on init to keep colors in sync
Option B — WebGLOverlayView + MVT Tiles (Full Control)
Render CLU geometries directly inside Google Maps using WebGLOverlayView and MVT tiles fetched individually. This approach keeps everything client-side and allows custom styling and interaction.
import { Loader } from '@googlemaps/js-api-loader';
import { VectorTile } from '@mapbox/vector-tile';
import Pbf from 'pbf';
const TILE_URL = 'https://api.landmapmagic.com/v1/tiles/clu/{z}/{x}/{y}.mvt?key=YOUR_API_KEY';
const loader = new Loader({
apiKey: process.env.GMAPS_KEY!,
version: 'weekly'
});
loader.load().then(() => {
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 google.maps.WebGLOverlayView();
overlay.onAdd = () => { overlay.requestRedraw(); };
overlay.onContextRestored = () => {
// Initialize shaders/buffers here.
overlay.requestRedraw();
};
overlay.onDraw = async ({ gl, transformer }) => {
const visibleTiles = transformer.getVisibleRegion().tiles();
for (const { z, x, y } of visibleTiles) {
const url = TILE_URL.replace('{z}', z).replace('{x}', x).replace('{y}', y);
const response = await fetch(url);
if (!response.ok) continue;
const data = await response.arrayBuffer();
const vt = new VectorTile(new Pbf(data));
const layer = vt.layers['clu'];
if (!layer) continue;
for (let i = 0; i < layer.length; i++) {
const feature = layer.feature(i);
const geom = feature.loadGeometry();
// Convert to Web Mercator, populate buffers, draw lines.
}
}
gl.drawArrays(gl.LINES, 0, vertexCount);
};
overlay.setMap(map);
});- Cache decoded tiles by z/x/y so panning does not refetch data
- Pull borderColor/hoverColor from /v1/styles/clu/style.json for visual parity with MapLibre
- Implement hover/click hit testing by checking whether pointer coordinates fall inside a decoded polygon
Option C — Pre-rendered Raster Tiles (Fallback)
If WebGL overlays are not an option, prerender CLU data to raster PNGs and register them with google.maps.ImageMapType. This removes interactivity but works with older Google Maps map IDs.
Performance & Quotas
- MVT tiles are edge-cached via Cloudflare — reuse URLs to maximize cache hits
- Throttle redraws to idle/tilesloaded events to avoid starving the main thread
- Rotate client-side API keys regularly and restrict allowed origins in the dashboard
Troubleshooting
- 401/403 responses — verify the key parameter and allowed origin configuration
- Empty overlays — ensure zoom level >= 11 (CLU tiles start at Z11)
- Visual artifacts — double-check coordinate conversions to Web Mercator