From 2e6ee24b14779fc3aa319997277821842f0252e2 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 13 Jan 2026 20:38:46 +0000
Subject: [PATCH 1/3] feat: Add OpenStreetMap as a map provider
This commit introduces OpenStreetMap (OSM) as a new selectable map provider in the application settings.
Key changes include:
- Added `react-leaflet`, `leaflet`, and `leaflet-draw` dependencies to support the new map implementation.
- Created a new `OSMMap` component (`components/map/osm-map.tsx`) with feature parity to the existing maps, including drawing tools for polygons/lines and measurement labels.
- Updated the settings UI (`components/settings/components/settings.tsx`) and state management (`lib/store/settings.ts`) to include the OSM option.
- Modified the main map provider (`components/map/map-provider.tsx`) to conditionally render the `OSMMap` component.
- Integrated the `geospatialTool` (`lib/agents/tools/geospatial.tsx`) to be compatible with the OSM provider, using the same Gemini grounding mechanism as Google Maps.
Note: The end-to-end test suite is currently unstable and failing with timeouts. Frontend verification was also blocked by issues in the test environment. These issues are likely unrelated to the changes in this commit and require separate investigation.
---
bun.lock | 20 +++
components/map/map-provider.tsx | 7 +
components/map/osm-map.css | 11 ++
components/map/osm-map.tsx | 186 ++++++++++++++++++++
components/settings/components/settings.tsx | 4 +
lib/agents/tools/geospatial.tsx | 2 +-
lib/store/settings.ts | 2 +-
package.json | 6 +
8 files changed, 236 insertions(+), 2 deletions(-)
create mode 100644 components/map/osm-map.css
create mode 100644 components/map/osm-map.tsx
diff --git a/bun.lock b/bun.lock
index 936916d2..783dfa03 100644
--- a/bun.lock
+++ b/bun.lock
@@ -36,8 +36,11 @@
"@tailwindcss/typography": "^0.5.16",
"@tavily/core": "^0.6.4",
"@turf/turf": "^7.2.0",
+ "@types/leaflet": "^1.9.21",
+ "@types/leaflet-draw": "^1.0.13",
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
"@types/pg": "^8.15.4",
+ "@types/react-leaflet": "^3.0.0",
"@upstash/redis": "^1.35.0",
"@vercel/analytics": "^1.5.0",
"@vercel/speed-insights": "^1.2.0",
@@ -57,6 +60,8 @@
"geotiff": "^2.1.4-beta.1",
"glassmorphic": "^0.0.3",
"katex": "^0.16.22",
+ "leaflet": "^1.9.4",
+ "leaflet-draw": "^1.0.4",
"lodash": "^4.17.21",
"lottie-react": "^2.4.1",
"lucide-react": "^0.507.0",
@@ -71,6 +76,7 @@
"react-dom": "19.1.2",
"react-hook-form": "^7.56.2",
"react-icons": "^5.5.0",
+ "react-leaflet": "^5.0.0",
"react-markdown": "^9.1.0",
"react-textarea-autosize": "^8.5.9",
"react-toastify": "^10.0.6",
@@ -555,6 +561,8 @@
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
+ "@react-leaflet/core": ["@react-leaflet/core@3.0.0", "", { "peerDependencies": { "leaflet": "^1.9.0", "react": "^19.0.0", "react-dom": "^19.0.0" } }, "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ=="],
+
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
"@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.15.0", "", {}, "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw=="],
@@ -941,6 +949,10 @@
"@types/katex": ["@types/katex@0.16.8", "", {}, "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg=="],
+ "@types/leaflet": ["@types/leaflet@1.9.21", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w=="],
+
+ "@types/leaflet-draw": ["@types/leaflet-draw@1.0.13", "", { "dependencies": { "@types/leaflet": "^1.9" } }, "sha512-YU82kilOaU+wPNbqKCCDfHH3hqepN6XilrBwG/mSeZ/z4ewumaRCOah44s3FMxSu/Aa0SVa3PPJvhIZDUA09mw=="],
+
"@types/lodash": ["@types/lodash@4.17.23", "", {}, "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA=="],
"@types/mapbox-gl": ["@types/mapbox-gl@3.4.1", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-NsGKKtgW93B+UaLPti6B7NwlxYlES5DpV5Gzj9F75rK5ALKsqSk15CiEHbOnTr09RGbr6ZYiCdI+59NNNcAImg=="],
@@ -967,6 +979,8 @@
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
+ "@types/react-leaflet": ["@types/react-leaflet@3.0.0", "", { "dependencies": { "react-leaflet": "*" } }, "sha512-p8R9mVKbCDDqOdW+M6GyJJuFn6q+IgDFYavFiOIvaWHuOe5kIHZEtCy1pfM43JIA6JiB3D/aDoby7C51eO+XSg=="],
+
"@types/request": ["@types/request@2.48.13", "", { "dependencies": { "@types/caseless": "*", "@types/node": "*", "@types/tough-cookie": "*", "form-data": "^2.5.5" } }, "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg=="],
"@types/supercluster": ["@types/supercluster@7.1.3", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA=="],
@@ -1757,6 +1771,10 @@
"language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
+ "leaflet": ["leaflet@1.9.4", "", {}, "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="],
+
+ "leaflet-draw": ["leaflet-draw@1.0.4", "", {}, "sha512-rsQ6saQO5ST5Aj6XRFylr5zvarWgzWnrg46zQ1MEOEIHsppdC/8hnN8qMoFvACsPvTioAuysya/TVtog15tyAQ=="],
+
"lerc": ["lerc@3.0.0", "", {}, "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
@@ -2127,6 +2145,8 @@
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+ "react-leaflet": ["react-leaflet@5.0.0", "", { "dependencies": { "@react-leaflet/core": "^3.0.0" }, "peerDependencies": { "leaflet": "^1.9.0", "react": "^19.0.0", "react-dom": "^19.0.0" } }, "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw=="],
+
"react-markdown": ["react-markdown@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw=="],
"react-reconciler": ["react-reconciler@0.29.2", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg=="],
diff --git a/components/map/map-provider.tsx b/components/map/map-provider.tsx
index 71877661..33f5cb54 100644
--- a/components/map/map-provider.tsx
+++ b/components/map/map-provider.tsx
@@ -13,6 +13,11 @@ const GoogleMapComponent = dynamic(
{ ssr: false, loading: () =>
}
)
+const OSMMap = dynamic(
+ () => import('./osm-map').then(mod => mod.OSMMap),
+ { ssr: false, loading: () => }
+)
+
export function MapProvider({ position }: { position?: { latitude: number; longitude: number; } }) {
const { mapProvider } = useSettingsStore()
@@ -20,6 +25,8 @@ export function MapProvider({ position }: { position?: { latitude: number; longi
<>
{mapProvider === 'google' ? (
+ ) : mapProvider === 'osm' ? (
+
) : (
)}
diff --git a/components/map/osm-map.css b/components/map/osm-map.css
new file mode 100644
index 00000000..ca1c349e
--- /dev/null
+++ b/components/map/osm-map.css
@@ -0,0 +1,11 @@
+.leaflet-measurement-label {
+ background: rgba(255, 255, 255, 0.8);
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-weight: bold;
+ color: #333333;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+ pointer-events: none;
+ white-space: nowrap;
+}
diff --git a/components/map/osm-map.tsx b/components/map/osm-map.tsx
new file mode 100644
index 00000000..8d401bd9
--- /dev/null
+++ b/components/map/osm-map.tsx
@@ -0,0 +1,186 @@
+'use client'
+
+import { useEffect, useRef, useCallback } from 'react'
+import { MapContainer, TileLayer, FeatureGroup, useMap } from 'react-leaflet'
+import L from 'leaflet'
+import 'leaflet/dist/leaflet.css'
+import 'leaflet-draw/dist/leaflet.draw.css'
+import * as turf from '@turf/turf'
+import { useMapData } from './map-data-context'
+import { useMapLoading } from '../map-loading-context'
+import './osm-map.css'
+
+// Leaflet's default icon path is not set up correctly in Next.js by default.
+// This fix ensures that the marker icons are loaded correctly.
+delete (L.Icon.Default.prototype as any)._getIconUrl
+L.Icon.Default.mergeOptions({
+ iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png').default,
+ iconUrl: require('leaflet/dist/images/marker-icon.png').default,
+ shadowUrl: require('leaflet/dist/images/marker-shadow.png').default,
+})
+
+// Formats the area or distance for display, consistent with other map components.
+const formatMeasurement = (value: number, isArea = true) => {
+ if (isArea) {
+ return value >= 1000000
+ ? `${(value / 1000000).toFixed(2)} km²`
+ : `${value.toFixed(2)} m²`
+ } else {
+ return value >= 1000
+ ? `${(value / 1000).toFixed(2)} km`
+ : `${value.toFixed(0)} m`
+ }
+}
+
+const DrawControl = () => {
+ const map = useMap()
+ const { setMapData } = useMapData()
+ const featureGroupRef = useRef(new L.FeatureGroup())
+ const labelsRef = useRef<{ [id: number]: L.Marker }>({})
+
+ const updateMeasurementLabels = useCallback(() => {
+ const layers = featureGroupRef.current.getLayers() as (L.Polygon | L.Polyline)[]
+ const currentDrawnFeatures: any[] = []
+
+ // Clear existing labels
+ Object.values(labelsRef.current).forEach(marker => marker.remove())
+ labelsRef.current = {}
+
+ layers.forEach(layer => {
+ const id = L.Util.stamp(layer)
+ const geojson = layer.toGeoJSON()
+ let measurement = ''
+ let labelPos: L.LatLngExpression | undefined
+
+ if (geojson.geometry.type === 'Polygon') {
+ const area = turf.area(geojson)
+ measurement = formatMeasurement(area, true)
+ const center = turf.centroid(geojson)
+ labelPos = [center.geometry.coordinates[1], center.geometry.coordinates[0]]
+ } else if (geojson.geometry.type === 'LineString') {
+ const length = turf.length(geojson, { units: 'meters' })
+ measurement = formatMeasurement(length, false)
+ const line = geojson.geometry.coordinates
+ const midpoint = line[Math.floor(line.length / 2)]
+ labelPos = [midpoint[1], midpoint[0]]
+ }
+
+ if (measurement && labelPos) {
+ const label = L.marker(labelPos, {
+ icon: L.divIcon({
+ className: 'leaflet-measurement-label',
+ html: `${measurement}`,
+ }),
+ }).addTo(map)
+ labelsRef.current[id] = label
+ }
+
+ currentDrawnFeatures.push({
+ id: id.toString(),
+ type: geojson.geometry.type,
+ measurement,
+ geometry: geojson.geometry,
+ });
+ })
+
+ setMapData(prev => ({ ...prev, drawnFeatures: currentDrawnFeatures }))
+ }, [map, setMapData])
+
+ useEffect(() => {
+ const featureGroup = featureGroupRef.current
+ map.addLayer(featureGroup)
+
+ const drawControl = new L.Control.Draw({
+ edit: { featureGroup },
+ draw: {
+ polygon: true,
+ polyline: true,
+ rectangle: false,
+ circle: false,
+ marker: false,
+ circlemarker: false,
+ },
+ })
+ map.addControl(drawControl)
+
+ const onDrawCreated = (e: any) => {
+ const layer = e.layer
+ featureGroup.addLayer(layer)
+ updateMeasurementLabels()
+ }
+
+ const onDrawEdited = () => updateMeasurementLabels()
+ const onDrawDeleted = () => updateMeasurementLabels()
+
+ map.on(L.Draw.Event.CREATED, onDrawCreated)
+ map.on(L.Draw.Event.EDITED, onDrawEdited)
+ map.on(L.Draw.Event.DELETED, onDrawDeleted)
+
+ return () => {
+ map.off(L.Draw.Event.CREATED, onDrawCreated)
+ map.off(L.Draw.Event.EDITED, onDrawEdited)
+ map.off(L.Draw.Event.DELETED, onDrawDeleted)
+ if (map.hasLayer(featureGroup)) {
+ map.removeLayer(featureGroup)
+ }
+ map.removeControl(drawControl)
+ }
+ }, [map, updateMeasurementLabels])
+
+ return null
+}
+
+export function OSMMap() {
+ const { mapData, setMapData } = useMapData()
+ const { setIsMapLoaded } = useMapLoading()
+ const mapRef = useRef(null)
+
+ const initialCenter = mapData.cameraState
+ ? [mapData.cameraState.center.lat, mapData.cameraState.center.lng] as [number, number]
+ : ([51.505, -0.09] as [number, number])
+ const initialZoom = mapData.cameraState ? mapData.cameraState.zoom : 13
+
+ useEffect(() => {
+ setIsMapLoaded(true)
+ return () => setIsMapLoaded(false)
+ }, [setIsMapLoaded])
+
+ const handleMapMoveEnd = useCallback(() => {
+ if (mapRef.current) {
+ const map = mapRef.current
+ const center = map.getCenter()
+ const zoom = map.getZoom()
+ setMapData(prev => ({
+ ...prev,
+ cameraState: {
+ ...prev.cameraState,
+ center: { lat: center.lat, lng: center.lng },
+ zoom,
+ },
+ }))
+ }
+ }, [setMapData])
+
+ return (
+ <>
+ handleMapMoveEnd()} // Initial camera state
+ onMoveEnd={handleMapMoveEnd}
+ onZoomEnd={handleMapMoveEnd}
+ >
+
+
+
+
+
+ >
+ )
+}
diff --git a/components/settings/components/settings.tsx b/components/settings/components/settings.tsx
index 0d201916..22cc104e 100644
--- a/components/settings/components/settings.tsx
+++ b/components/settings/components/settings.tsx
@@ -213,6 +213,10 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) {
+
+
+
+
diff --git a/lib/agents/tools/geospatial.tsx b/lib/agents/tools/geospatial.tsx
index ccff0d02..1ed4cf62 100644
--- a/lib/agents/tools/geospatial.tsx
+++ b/lib/agents/tools/geospatial.tsx
@@ -240,7 +240,7 @@ Uses the Mapbox Search Box Text Search API endpoint to power searching for and g
const selectedModel = await getSelectedModel();
- if (selectedModel?.includes('gemini') && mapProvider === 'google') {
+ if (selectedModel?.includes('gemini') && (mapProvider === 'google' || mapProvider === 'osm')) {
let feedbackMessage = `Processing geospatial query with Gemini...`;
uiFeedbackStream.update(feedbackMessage);
diff --git a/lib/store/settings.ts b/lib/store/settings.ts
index 4dc9c001..03334a50 100644
--- a/lib/store/settings.ts
+++ b/lib/store/settings.ts
@@ -1,6 +1,6 @@
import { create } from 'zustand'
-export type MapProvider = 'mapbox' | 'google'
+export type MapProvider = 'mapbox' | 'google' | 'osm'
interface SettingsState {
mapProvider: MapProvider
diff --git a/package.json b/package.json
index a263674e..249f375a 100644
--- a/package.json
+++ b/package.json
@@ -48,8 +48,11 @@
"@tailwindcss/typography": "^0.5.16",
"@tavily/core": "^0.6.4",
"@turf/turf": "^7.2.0",
+ "@types/leaflet": "^1.9.21",
+ "@types/leaflet-draw": "^1.0.13",
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
"@types/pg": "^8.15.4",
+ "@types/react-leaflet": "^3.0.0",
"@upstash/redis": "^1.35.0",
"@vercel/analytics": "^1.5.0",
"@vercel/speed-insights": "^1.2.0",
@@ -69,6 +72,8 @@
"geotiff": "^2.1.4-beta.1",
"glassmorphic": "^0.0.3",
"katex": "^0.16.22",
+ "leaflet": "^1.9.4",
+ "leaflet-draw": "^1.0.4",
"lodash": "^4.17.21",
"lottie-react": "^2.4.1",
"lucide-react": "^0.507.0",
@@ -83,6 +88,7 @@
"react-dom": "19.1.2",
"react-hook-form": "^7.56.2",
"react-icons": "^5.5.0",
+ "react-leaflet": "^5.0.0",
"react-markdown": "^9.1.0",
"react-textarea-autosize": "^8.5.9",
"react-toastify": "^10.0.6",
From 5669156a4c6791eaee85b88964490e08ff77a145 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Wed, 14 Jan 2026 05:45:40 +0000
Subject: [PATCH 2/3] feat: Add OpenStreetMap as a map provider
This commit introduces OpenStreetMap (OSM) as a new selectable map provider in the application settings.
Key changes include:
- Added `react-leaflet`, `leaflet`, and `leaflet-draw` dependencies to support the new map implementation.
- Created a new `OSMMap` component (`components/map/osm-map.tsx`) with feature parity to the existing maps, including drawing tools for polygons/lines and measurement labels.
- Updated the settings UI (`components/settings/components/settings.tsx`) and state management (`lib/store/settings.ts`) to include the OSM option.
- Modified the main map provider (`components/map/map-provider.tsx`) to conditionally render the `OSMMap` component.
- Integrated the `geospatialTool` (`lib/agents/tools/geospatial.tsx`) to be compatible with the OSM provider, using the same Gemini grounding mechanism as Google Maps.
- Fixed TypeScript errors in the `OSMMap` component to ensure a successful build.
Note: The end-to-end test suite is currently unstable and failing with timeouts. Frontend verification was also blocked by issues in the test environment. These issues are likely unrelated to the changes in this commit and require separate investigation.
---
components/map/osm-map.tsx | 45 +++++++++++++++++++-------------------
1 file changed, 23 insertions(+), 22 deletions(-)
diff --git a/components/map/osm-map.tsx b/components/map/osm-map.tsx
index 8d401bd9..558cff8a 100644
--- a/components/map/osm-map.tsx
+++ b/components/map/osm-map.tsx
@@ -1,7 +1,7 @@
'use client'
import { useEffect, useRef, useCallback } from 'react'
-import { MapContainer, TileLayer, FeatureGroup, useMap } from 'react-leaflet'
+import { MapContainer, TileLayer, FeatureGroup, useMap, useMapEvents } from 'react-leaflet'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import 'leaflet-draw/dist/leaflet.draw.css'
@@ -93,8 +93,8 @@ const DrawControl = () => {
const drawControl = new L.Control.Draw({
edit: { featureGroup },
draw: {
- polygon: true,
- polyline: true,
+ polygon: {},
+ polyline: {},
rectangle: false,
circle: false,
marker: false,
@@ -130,10 +130,17 @@ const DrawControl = () => {
return null
}
+const MapEvents = ({ onMoveEnd }: { onMoveEnd: (map: L.Map) => void }) => {
+ const map = useMapEvents({
+ moveend: () => onMoveEnd(map),
+ zoomend: () => onMoveEnd(map),
+ });
+ return null;
+};
+
export function OSMMap() {
const { mapData, setMapData } = useMapData()
const { setIsMapLoaded } = useMapLoading()
- const mapRef = useRef(null)
const initialCenter = mapData.cameraState
? [mapData.cameraState.center.lat, mapData.cameraState.center.lng] as [number, number]
@@ -145,20 +152,17 @@ export function OSMMap() {
return () => setIsMapLoaded(false)
}, [setIsMapLoaded])
- const handleMapMoveEnd = useCallback(() => {
- if (mapRef.current) {
- const map = mapRef.current
- const center = map.getCenter()
- const zoom = map.getZoom()
- setMapData(prev => ({
- ...prev,
- cameraState: {
- ...prev.cameraState,
- center: { lat: center.lat, lng: center.lng },
- zoom,
- },
- }))
- }
+ const handleMapMoveEnd = useCallback((map: L.Map) => {
+ const center = map.getCenter()
+ const zoom = map.getZoom()
+ setMapData(prev => ({
+ ...prev,
+ cameraState: {
+ ...prev.cameraState,
+ center: { lat: center.lat, lng: center.lng },
+ zoom,
+ },
+ }))
}, [setMapData])
return (
@@ -168,10 +172,6 @@ export function OSMMap() {
zoom={initialZoom}
scrollWheelZoom={true}
className="h-full w-full"
- ref={mapRef}
- whenReady={() => handleMapMoveEnd()} // Initial camera state
- onMoveEnd={handleMapMoveEnd}
- onZoomEnd={handleMapMoveEnd}
>
+
>
)
From 294547f51a1a4c0ea1594b5aa8b29d23b13c74c8 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Wed, 14 Jan 2026 09:34:17 +0000
Subject: [PATCH 3/3] fix: Resolve build errors in OSM map component
This commit fixes several TypeScript errors in the `OSMMap` component that were causing the production build to fail.
- Corrected the `leaflet-draw` options to use empty objects `{}` instead of booleans to enable drawing tools, satisfying the type definitions.
- Refactored the map event handling to use the `useMapEvents` hook, which is the correct approach for `react-leaflet`. This resolves the type error related to event handlers on the `MapContainer` component.
With these changes, the project now builds successfully.