google map as default and auto zoom
Some checks failed
Build and deploy / Build and Push Docker Images (push) Failing after 39s
Some checks failed
Build and deploy / Build and Push Docker Images (push) Failing after 39s
This commit is contained in:
@@ -6,6 +6,7 @@ import { DrawingHandler } from './DrawingHandler';
|
|||||||
import { MapItemsLayer } from './MapItemsLayer';
|
import { MapItemsLayer } from './MapItemsLayer';
|
||||||
import { ShareDialog } from './ShareDialog';
|
import { ShareDialog } from './ShareDialog';
|
||||||
import { useMapWebSocket } from '../../hooks/useMapWebSocket';
|
import { useMapWebSocket } from '../../hooks/useMapWebSocket';
|
||||||
|
import { mapItemService } from '../../services/mapItemService';
|
||||||
|
|
||||||
// Fix Leaflet's default icon paths for production builds
|
// Fix Leaflet's default icon paths for production builds
|
||||||
// Since we use custom DivIcons, we just need to prevent 404s
|
// Since we use custom DivIcons, we just need to prevent 404s
|
||||||
@@ -38,6 +39,49 @@ function MapController() {
|
|||||||
return null;
|
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) {
|
export function MapView({ mapId, activeLayer, mapLayers, showShareDialog = false, shareMapId, onCloseShareDialog }: MapViewProps) {
|
||||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||||
|
|
||||||
@@ -89,6 +133,7 @@ export function MapView({ mapId, activeLayer, mapLayers, showShareDialog = false
|
|||||||
style={{ background: '#f0f0f0' }}
|
style={{ background: '#f0f0f0' }}
|
||||||
>
|
>
|
||||||
<MapController />
|
<MapController />
|
||||||
|
<AutoZoom mapId={mapId} refreshTrigger={refreshTrigger} />
|
||||||
<TileLayer
|
<TileLayer
|
||||||
key={activeLayer}
|
key={activeLayer}
|
||||||
url={layer.url}
|
url={layer.url}
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ import { LayerSwitcher } from '../components/map/LayerSwitcher';
|
|||||||
type MapLayer = 'osm' | 'google' | 'esri';
|
type MapLayer = 'osm' | 'google' | 'esri';
|
||||||
|
|
||||||
const MAP_LAYERS = {
|
const MAP_LAYERS = {
|
||||||
osm: {
|
|
||||||
name: 'OpenStreetMap',
|
|
||||||
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
|
||||||
maxZoom: 25,
|
|
||||||
},
|
|
||||||
google: {
|
google: {
|
||||||
name: 'Google Satellite',
|
name: 'Google Satellite',
|
||||||
url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
||||||
@@ -21,6 +15,12 @@ const MAP_LAYERS = {
|
|||||||
maxZoom: 25,
|
maxZoom: 25,
|
||||||
maxNativeZoom: 22,
|
maxNativeZoom: 22,
|
||||||
},
|
},
|
||||||
|
osm: {
|
||||||
|
name: 'OpenStreetMap',
|
||||||
|
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
|
maxZoom: 25,
|
||||||
|
},
|
||||||
esri: {
|
esri: {
|
||||||
name: 'ESRI Satellite',
|
name: 'ESRI Satellite',
|
||||||
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
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<string | null>(null);
|
const [selectedMapId, setSelectedMapId] = useState<string | null>(null);
|
||||||
const [showShareDialog, setShowShareDialog] = useState(false);
|
const [showShareDialog, setShowShareDialog] = useState(false);
|
||||||
const [shareMapId, setShareMapId] = useState<string | null>(null);
|
const [shareMapId, setShareMapId] = useState<string | null>(null);
|
||||||
const [activeLayer, setActiveLayer] = useState<MapLayer>('osm');
|
const [activeLayer, setActiveLayer] = useState<MapLayer>('google');
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||||
|
|
||||||
const handleShareMap = (mapId: string) => {
|
const handleShareMap = (mapId: string) => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { Toolbar } from '../components/map/Toolbar';
|
|||||||
import { useMapWebSocket } from '../hooks/useMapWebSocket';
|
import { useMapWebSocket } from '../hooks/useMapWebSocket';
|
||||||
import { apiClient } from '../services/api';
|
import { apiClient } from '../services/api';
|
||||||
import { useUIStore } from '../stores/uiStore';
|
import { useUIStore } from '../stores/uiStore';
|
||||||
|
import { mapItemService } from '../services/mapItemService';
|
||||||
|
|
||||||
// Fix Leaflet's default icon paths for production builds
|
// Fix Leaflet's default icon paths for production builds
|
||||||
// Since we use custom DivIcons, we just need to prevent 404s
|
// Since we use custom DivIcons, we just need to prevent 404s
|
||||||
@@ -23,12 +24,6 @@ L.Icon.Default.mergeOptions({
|
|||||||
type MapLayer = 'osm' | 'google' | 'esri';
|
type MapLayer = 'osm' | 'google' | 'esri';
|
||||||
|
|
||||||
const MAP_LAYERS = {
|
const MAP_LAYERS = {
|
||||||
osm: {
|
|
||||||
name: 'OpenStreetMap',
|
|
||||||
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
|
||||||
maxZoom: 25,
|
|
||||||
},
|
|
||||||
google: {
|
google: {
|
||||||
name: 'Google Satellite',
|
name: 'Google Satellite',
|
||||||
url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
url: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
||||||
@@ -36,6 +31,12 @@ const MAP_LAYERS = {
|
|||||||
maxZoom: 25,
|
maxZoom: 25,
|
||||||
maxNativeZoom: 22,
|
maxNativeZoom: 22,
|
||||||
},
|
},
|
||||||
|
osm: {
|
||||||
|
name: 'OpenStreetMap',
|
||||||
|
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
|
maxZoom: 25,
|
||||||
|
},
|
||||||
esri: {
|
esri: {
|
||||||
name: 'ESRI Satellite',
|
name: 'ESRI Satellite',
|
||||||
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
||||||
@@ -56,10 +57,53 @@ function MapController() {
|
|||||||
return null;
|
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() {
|
export function SharedMap() {
|
||||||
const { token } = useParams<{ token: string }>();
|
const { token } = useParams<{ token: string }>();
|
||||||
const { darkMode, toggleDarkMode } = useUIStore();
|
const { darkMode, toggleDarkMode } = useUIStore();
|
||||||
const [activeLayer, setActiveLayer] = useState<MapLayer>('osm');
|
const [activeLayer, setActiveLayer] = useState<MapLayer>('google');
|
||||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||||
const [mapData, setMapData] = useState<any>(null);
|
const [mapData, setMapData] = useState<any>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -232,6 +276,7 @@ export function SharedMap() {
|
|||||||
style={{ background: '#f0f0f0' }}
|
style={{ background: '#f0f0f0' }}
|
||||||
>
|
>
|
||||||
<MapController />
|
<MapController />
|
||||||
|
<AutoZoom mapId={mapData.id} refreshTrigger={refreshTrigger} />
|
||||||
<TileLayer
|
<TileLayer
|
||||||
key={activeLayer}
|
key={activeLayer}
|
||||||
url={MAP_LAYERS[activeLayer].url}
|
url={MAP_LAYERS[activeLayer].url}
|
||||||
|
|||||||
Reference in New Issue
Block a user