From 6a9fe5d26f3c78cbbbaa9959403745af9881ac06 Mon Sep 17 00:00:00 2001 From: Logan Date: Mon, 25 May 2026 13:41:10 -0400 Subject: [PATCH] feat: replace Google tile URL hack with leaflet-google-mutant for traffic layer Add leaflet-google-mutant@0.16.0 (exact/locked version) as a proper bridge between the Google Maps JavaScript API and Leaflet. The old mt{s}.google.com tile URL approach was unofficial and produced empty tiles. Traffic layer now renders via createLayerComponent + googleMutant, loaded only after the Maps JS API script is injected and ready (keyed off NEXT_PUBLIC_GOOGLE_MAPS_API_KEY). Added leaflet-google-mutant to transpilePackages in next.config.mjs. --- drb-frontend/components/MapView.tsx | 39 +++++++++++++++++++++++------ drb-frontend/next.config.mjs | 2 +- drb-frontend/package.json | 1 + 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/drb-frontend/components/MapView.tsx b/drb-frontend/components/MapView.tsx index ea24d3f..f46a94e 100644 --- a/drb-frontend/components/MapView.tsx +++ b/drb-frontend/components/MapView.tsx @@ -10,7 +10,9 @@ import { TileLayer, useMap, } from "react-leaflet"; +import { createLayerComponent } from "@react-leaflet/core"; import L from "leaflet"; +import "leaflet-google-mutant"; import type { CallRecord, IncidentRecord, NodeRecord } from "@/lib/types"; // ── Leaflet icon fix ────────────────────────────────────────────────────────── @@ -145,6 +147,19 @@ function computeGroups( return result; } +// ── Google Maps traffic layer via leaflet-google-mutant ─────────────────────── +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const GoogleTrafficOverlay = createLayerComponent( + (_props, ctx) => { + // leaflet-google-mutant augments L.gridLayer after the side-effect import + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const instance = (L as any).gridLayer.googleMutant({ type: "roadmap" }); + instance.addGoogleLayer("TrafficLayer"); + return { instance, context: ctx }; + }, + () => {} +); + // ── MapRefCapture — exposes L.Map instance to parent ───────────────────────── function MapRefCapture({ onReady }: { onReady: (m: L.Map) => void }) { const map = useMap(); @@ -342,6 +357,21 @@ export default function MapView({ nodes, activeCalls, incidents = [], lastUpdate return () => clearInterval(id); }, []); + // Load Google Maps JS API for leaflet-google-mutant traffic layer + const [googleReady, setGoogleReady] = useState(false); + useEffect(() => { + const key = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY; + if (!key) return; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((window as any).google?.maps) { setGoogleReady(true); return; } + if (document.querySelector('script[src*="maps.googleapis.com/maps/api/js"]')) return; + const script = document.createElement("script"); + script.src = `https://maps.googleapis.com/maps/api/js?key=${key}`; + script.async = true; + script.onload = () => setGoogleReady(true); + document.head.appendChild(script); + }, []); + // Live clock for TOC situational awareness useEffect(() => { const id = setInterval(() => @@ -438,14 +468,9 @@ export default function MapView({ nodes, activeCalls, incidents = [], lastUpdate - {/* Overlay: Traffic — Google Maps traffic layer */} + {/* Overlay: Traffic — Google Maps via leaflet-google-mutant */} - + {googleReady ? : } {/* Overlay: Weather Radar — NEXRAD via Iowa Env Mesonet; key forces remount on refresh */} diff --git a/drb-frontend/next.config.mjs b/drb-frontend/next.config.mjs index 0242696..e9e3808 100644 --- a/drb-frontend/next.config.mjs +++ b/drb-frontend/next.config.mjs @@ -1,7 +1,7 @@ /** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone", - transpilePackages: ["leaflet", "react-leaflet"], + transpilePackages: ["leaflet", "react-leaflet", "leaflet-google-mutant"], }; export default nextConfig; diff --git a/drb-frontend/package.json b/drb-frontend/package.json index 8558f7c..56b6a22 100644 --- a/drb-frontend/package.json +++ b/drb-frontend/package.json @@ -14,6 +14,7 @@ "react-dom": "^18.3.0", "firebase": "^10.12.0", "leaflet": "^1.9.4", + "leaflet-google-mutant": "0.16.0", "react-leaflet": "^4.2.1" }, "devDependencies": {