diff --git a/app/src/api.js b/app/src/api.js index 3c8ab85e..a0e87203 100644 --- a/app/src/api.js +++ b/app/src/api.js @@ -317,6 +317,7 @@ export async function getSettings() { ignore_outdated_zims: content.ignore_outdated_zims, ignored_directories: content.ignored_directories, log_level: content.log_level, + map_default_location: content.map_default_location, map_destination: content.map_destination, nav_color: content.nav_color, media_directory: content.media_directory, @@ -1568,6 +1569,17 @@ export async function updateMapPin(pinId, label, color) { } } +export async function setMapDefaultLocation(lat, lon, zoom) { + try { + await updateConfig('wrolpi.yaml', {map_default_location: {lat, lon, zoom}}); + toast({type: 'success', title: 'Default Location', description: 'Default map location saved', time: 3000}); + return true; + } catch (e) { + toast({type: 'error', title: 'Map Error', description: 'Could not set default location', time: 5000}); + return false; + } +} + export async function clearCompletedDownloads() { const response = await apiPost(`${API_URI}/download/clear_completed`); if (!response.ok) { diff --git a/app/src/components/MapViewer.js b/app/src/components/MapViewer.js index 3b84b6bb..fac3910b 100644 --- a/app/src/components/MapViewer.js +++ b/app/src/components/MapViewer.js @@ -5,11 +5,11 @@ import "maplibre-gl/dist/maplibre-gl.css"; import {Protocol} from "pmtiles"; import layers from "protomaps-themes-base"; import mlcontour from "@acalcutt/maplibre-contour-pmtiles"; -import {addMapPin, deleteMapPin, getMapFiles, getMapPins} from "../api"; +import {addMapPin, deleteMapPin, getMapFiles, getMapPins, setMapDefaultLocation} from "../api"; import {MAP_VIEWER_URI} from "./Vars"; import {Checkbox, Icon, Segment} from "semantic-ui-react"; import {Header} from "./Theme"; -import {ThemeContext} from "../contexts/contexts"; +import {SettingsContext, ThemeContext} from "../contexts/contexts"; // Terrain DEM file prefix for hillshade and contours. const TERRAIN_PREFIX = "terrain-"; @@ -354,11 +354,20 @@ export default function MapViewer() { }); const [searchParams, setSearchParams] = useSearchParams(); const {theme} = useContext(ThemeContext); + const {settings, fetchSettings} = useContext(SettingsContext); - // Parse initial coordinates from URL. - const initialLat = parseFloat(searchParams.get("lat")) || 0; - const initialLon = parseFloat(searchParams.get("lon")) || 0; - const initialZoom = parseFloat(searchParams.get("z")) || 2; + // Use URL params if provided, otherwise fall back to saved default location, then 0,0,z2. + const hasUrlParams = searchParams.has("lat") || searchParams.has("lon") || searchParams.has("z"); + const defaultLoc = settings?.map_default_location; + const initialLat = hasUrlParams + ? (parseFloat(searchParams.get("lat")) || 0) + : (parseFloat(defaultLoc?.lat) || 0); + const initialLon = hasUrlParams + ? (parseFloat(searchParams.get("lon")) || 0) + : (parseFloat(defaultLoc?.lon) || 0); + const initialZoom = hasUrlParams + ? (parseFloat(searchParams.get("z")) || 2) + : (parseFloat(defaultLoc?.zoom) || 2); useEffect(() => { if (!mapContainer.current) return; @@ -705,6 +714,16 @@ export default function MapViewer() { setContextMenu(null); }, [contextMenu]); + const handleSetDefaultLocation = useCallback(async () => { + const map = mapRef.current; + if (!map) return; + const center = map.getCenter(); + const zoom = map.getZoom(); + await setMapDefaultLocation(center.lat, center.lng, zoom); + await fetchSettings(); + setContextMenu(null); + }, [fetchSettings]); + const handleSubmitPin = useCallback(async (label, color) => { if (!addingPin) return; const {lat, lon} = addingPin; @@ -770,6 +789,14 @@ export default function MapViewer() { > Add Pin Here +