From e36e5e8fc5d2d406f7dd284bb5aaa61011fc6e39 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Sun, 14 Dec 2025 23:19:20 +0500 Subject: [PATCH] google map as default and auto zoom --- public/src/components/map/MapView.tsx | 45 ++++++++++++++++++++ public/src/pages/Dashboard.tsx | 14 +++---- public/src/pages/SharedMap.tsx | 59 +++++++++++++++++++++++---- 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/public/src/components/map/MapView.tsx b/public/src/components/map/MapView.tsx index acd58b0..5b16df0 100644 --- a/public/src/components/map/MapView.tsx +++ b/public/src/components/map/MapView.tsx @@ -6,6 +6,7 @@ import { DrawingHandler } from './DrawingHandler'; import { MapItemsLayer } from './MapItemsLayer'; import { ShareDialog } from './ShareDialog'; import { useMapWebSocket } from '../../hooks/useMapWebSocket'; +import { mapItemService } from '../../services/mapItemService'; // Fix Leaflet's default icon paths for production builds // Since we use custom DivIcons, we just need to prevent 404s @@ -38,6 +39,49 @@ function MapController() { return null; } +function AutoZoom({ mapId, refreshTrigger }: { mapId: string; refreshTrigger: number }) { + const map = useMap(); + + useEffect(() => { + const zoomToDevices = async () => { + try { + const items = await mapItemService.getMapItems(mapId); + + // Filter only devices (exclude cables, wireless mesh, and info markers) + const devices = items.filter(item => + ['switch', 'indoor_ap', 'outdoor_ap', 'other_device'].includes(item.type) && + item.geometry.type === 'Point' + ); + + if (devices.length === 0) { + // No devices, keep default view + return; + } + + // Create bounds from all device coordinates + const bounds = L.latLngBounds( + devices.map(device => { + const [lng, lat] = device.geometry.coordinates; + return [lat, lng] as [number, number]; + }) + ); + + // Fit map to bounds with padding + map.fitBounds(bounds, { + padding: [50, 50], + maxZoom: 18, + }); + } catch (error) { + console.error('Failed to auto-zoom to devices:', error); + } + }; + + zoomToDevices(); + }, [mapId, refreshTrigger, map]); + + return null; +} + export function MapView({ mapId, activeLayer, mapLayers, showShareDialog = false, shareMapId, onCloseShareDialog }: MapViewProps) { const [refreshTrigger, setRefreshTrigger] = useState(0); @@ -89,6 +133,7 @@ export function MapView({ mapId, activeLayer, mapLayers, showShareDialog = false style={{ background: '#f0f0f0' }} > + OpenStreetMap contributors', - maxZoom: 25, - }, google: { name: 'Google Satellite', url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', @@ -21,6 +15,12 @@ const MAP_LAYERS = { maxZoom: 25, maxNativeZoom: 22, }, + osm: { + name: 'OpenStreetMap', + url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + attribution: '© OpenStreetMap contributors', + maxZoom: 25, + }, esri: { name: 'ESRI Satellite', url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', @@ -33,7 +33,7 @@ export function Dashboard() { const [selectedMapId, setSelectedMapId] = useState(null); const [showShareDialog, setShowShareDialog] = useState(false); const [shareMapId, setShareMapId] = useState(null); - const [activeLayer, setActiveLayer] = useState('osm'); + const [activeLayer, setActiveLayer] = useState('google'); const [isSidebarOpen, setIsSidebarOpen] = useState(false); const handleShareMap = (mapId: string) => { diff --git a/public/src/pages/SharedMap.tsx b/public/src/pages/SharedMap.tsx index 32d5e69..eb25da2 100644 --- a/public/src/pages/SharedMap.tsx +++ b/public/src/pages/SharedMap.tsx @@ -10,6 +10,7 @@ import { Toolbar } from '../components/map/Toolbar'; import { useMapWebSocket } from '../hooks/useMapWebSocket'; import { apiClient } from '../services/api'; import { useUIStore } from '../stores/uiStore'; +import { mapItemService } from '../services/mapItemService'; // Fix Leaflet's default icon paths for production builds // Since we use custom DivIcons, we just need to prevent 404s @@ -23,12 +24,6 @@ L.Icon.Default.mergeOptions({ type MapLayer = 'osm' | 'google' | 'esri'; const MAP_LAYERS = { - osm: { - name: 'OpenStreetMap', - url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', - attribution: '© OpenStreetMap contributors', - maxZoom: 25, - }, google: { name: 'Google Satellite', url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', @@ -36,6 +31,12 @@ const MAP_LAYERS = { maxZoom: 25, maxNativeZoom: 22, }, + osm: { + name: 'OpenStreetMap', + url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + attribution: '© OpenStreetMap contributors', + maxZoom: 25, + }, esri: { name: 'ESRI Satellite', url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', @@ -56,10 +57,53 @@ function MapController() { return null; } +function AutoZoom({ mapId, refreshTrigger }: { mapId: string; refreshTrigger: number }) { + const map = useMap(); + + useEffect(() => { + const zoomToDevices = async () => { + try { + const items = await mapItemService.getMapItems(mapId); + + // Filter only devices (exclude cables, wireless mesh, and info markers) + const devices = items.filter(item => + ['switch', 'indoor_ap', 'outdoor_ap', 'other_device'].includes(item.type) && + item.geometry.type === 'Point' + ); + + if (devices.length === 0) { + // No devices, keep default view + return; + } + + // Create bounds from all device coordinates + const bounds = L.latLngBounds( + devices.map(device => { + const [lng, lat] = device.geometry.coordinates; + return [lat, lng] as [number, number]; + }) + ); + + // Fit map to bounds with padding + map.fitBounds(bounds, { + padding: [50, 50], + maxZoom: 18, + }); + } catch (error) { + console.error('Failed to auto-zoom to devices:', error); + } + }; + + zoomToDevices(); + }, [mapId, refreshTrigger, map]); + + return null; +} + export function SharedMap() { const { token } = useParams<{ token: string }>(); const { darkMode, toggleDarkMode } = useUIStore(); - const [activeLayer, setActiveLayer] = useState('osm'); + const [activeLayer, setActiveLayer] = useState('google'); const [refreshTrigger, setRefreshTrigger] = useState(0); const [mapData, setMapData] = useState(null); const [loading, setLoading] = useState(true); @@ -232,6 +276,7 @@ export function SharedMap() { style={{ background: '#f0f0f0' }} > +