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 { 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' }}
|
||||
>
|
||||
<MapController />
|
||||
<AutoZoom mapId={mapId} refreshTrigger={refreshTrigger} />
|
||||
<TileLayer
|
||||
key={activeLayer}
|
||||
url={layer.url}
|
||||
|
||||
@@ -8,12 +8,6 @@ import { LayerSwitcher } from '../components/map/LayerSwitcher';
|
||||
type MapLayer = 'osm' | 'google' | 'esri';
|
||||
|
||||
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: {
|
||||
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: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> 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<string | null>(null);
|
||||
const [showShareDialog, setShowShareDialog] = useState(false);
|
||||
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 handleShareMap = (mapId: string) => {
|
||||
|
||||
@@ -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: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> 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: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> 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<MapLayer>('osm');
|
||||
const [activeLayer, setActiveLayer] = useState<MapLayer>('google');
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||
const [mapData, setMapData] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -232,6 +276,7 @@ export function SharedMap() {
|
||||
style={{ background: '#f0f0f0' }}
|
||||
>
|
||||
<MapController />
|
||||
<AutoZoom mapId={mapData.id} refreshTrigger={refreshTrigger} />
|
||||
<TileLayer
|
||||
key={activeLayer}
|
||||
url={MAP_LAYERS[activeLayer].url}
|
||||
|
||||
Reference in New Issue
Block a user