Adding map default view option to the map context menu.

This commit is contained in:
Roland 2026-04-04 14:56:25 -06:00
parent b8a532c8d9
commit bbc23dbf90
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
5 changed files with 79 additions and 6 deletions

View file

@ -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) {

View file

@ -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
</div>
<div
style={{padding: "8px 12px", cursor: "pointer", userSelect: "none"}}
onMouseEnter={e => e.target.style.background = "#f0f0f0"}
onMouseLeave={e => e.target.style.background = "transparent"}
onClick={handleSetDefaultLocation}
>
Set as Default View
</div>
</div>}
{addingPin && <AddPinDialog
lat={addingPin.lat}

View file

@ -128,3 +128,26 @@ async def test_subscribe_invalid_region(async_client, test_session, test_directo
body = json.dumps({'name': 'Atlantis', 'region': 'atlantis'})
request, response = await async_client.post('/api/map/subscribe', content=body)
assert response.status_code == HTTPStatus.BAD_REQUEST
@pytest.mark.asyncio
async def test_map_default_location_empty(async_client, test_directory):
"""Default location is null when not set."""
request, response = await async_client.get('/api/settings')
assert response.status_code == HTTPStatus.OK
assert response.json['map_default_location'] is None
@pytest.mark.asyncio
async def test_set_and_get_map_default_location(async_client, test_directory):
"""Setting a default map location persists and is returned in settings."""
body = json.dumps({'config': {'map_default_location': {'lat': 40.7, 'lon': -111.9, 'zoom': 10.5}}})
request, response = await async_client.post('/api/config?file_name=wrolpi.yaml', content=body)
assert response.status_code == HTTPStatus.NO_CONTENT
request, response = await async_client.get('/api/settings')
assert response.status_code == HTTPStatus.OK
loc = response.json['map_default_location']
assert loc['lat'] == 40.7
assert loc['lon'] == -111.9
assert loc['zoom'] == 10.5

View file

@ -801,6 +801,7 @@ class WROLPiConfigValidator:
hotspot_ssid: str = None
ignore_outdated_zims: bool = None
log_level: str = None
map_default_location: dict = None
map_destination: str = None
nav_color: str = None
require_cookies_unlocked: bool = None
@ -979,6 +980,7 @@ class WROLPiConfig(ConfigFile):
ignore_outdated_zims=False,
ignored_directories=['config', 'tags'],
log_level='info',
map_default_location=None,
map_destination='map',
nav_color='violet',
require_cookies_unlocked=True,
@ -1197,6 +1199,14 @@ class WROLPiConfig(ConfigFile):
def archive_destination(self, value: str):
self.update({'archive_destination': value})
@property
def map_default_location(self) -> dict:
return self._config.get('map_default_location')
@map_default_location.setter
def map_default_location(self, value: dict):
self.update({'map_default_location': value})
@property
def map_destination(self) -> str:
return self._config['map_destination']

View file

@ -171,6 +171,7 @@ def get_settings(_: Request):
'ignore_outdated_zims': wrolpi_config.ignore_outdated_zims,
'ignored_directories': ignored_directories,
'log_level': api_app.shared_ctx.log_level.value,
'map_default_location': wrolpi_config.map_default_location,
'map_destination': wrolpi_config.map_destination,
'nav_color': wrolpi_config.nav_color,
'media_directory': str(get_media_directory()), # Convert to string to avoid conversion to relative.