From 6300de22f072e973167445695ae835ed90015aeb Mon Sep 17 00:00:00 2001 From: Mark Kalsbeek Date: Sun, 25 Jan 2026 12:55:30 +0100 Subject: [PATCH] fixes here and there --- ui/src/App.vue | 249 +++++----------------- ui/src/components/ContextMenu.vue | 295 ++++++++++++++++++++++----- ui/src/components/ShadingSandbox.vue | 141 ++++++------- ui/src/utils/coordinates.js | 10 + 4 files changed, 366 insertions(+), 329 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 0a201db..6a5b928 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -30,7 +30,6 @@ v-model:imperialUnits="imperialUnits" :visibleSites="visibleSites" :allHistoricMarkersHidden="allHistoricMarkersHidden" - :tileRequests="tileRequests" @toggleSite="toggleSite" @jumpToSite="jumpToSite" @toggleAllSites="hideAllHistoricMarkers" @@ -39,7 +38,6 @@ @update:lidarOpacity="updateLidarOpacity" @update:showLidar="toggleLidar" @update:showGeometry="toggleGeometry" - @dismissRequest="dismissTileRequest" /> @@ -56,20 +54,12 @@ @@ -122,6 +112,7 @@ import { KNOWN_SITES } from './data/historicSites.js'; // UTILITIES // ============================================================================ import { calculateDistance, calculateBearing, extendRay } from './utils/geometry.js'; +import { webMercatorToLonLat } from './utils/coordinates.js'; import { useTilesStore } from './stores/tiles.js'; // For generating the pre-baked tiles: @@ -162,7 +153,7 @@ const sandboxRef = ref(null); const sandboxVisible = ref(false); const sandboxOffscreen = ref(false); const baseLayer = ref('osm'); -const historicMarkersExpanded = ref(true); +const historicMarkersExpanded = ref(false); const visibleSites = ref({}); const allHistoricMarkersHidden = ref(false); const showLidar = ref(true); @@ -175,10 +166,6 @@ const lidarOpacity = ref(80); // ============================================================================ const currentTileData = ref(null); -// Tile request tracking: { requestId: { lat, lng, status, message, tileId } } -const tileRequests = ref({}); -let nextRequestId = 1; - // ============================================================================ // REFS - Geometry State // ============================================================================ @@ -191,16 +178,7 @@ const nextFeatureId = ref(1); // ============================================================================ // REFS - UI Overlays // ============================================================================ -const contextMenu = ref({ - visible: false, - x: 0, - y: 0, - lngLat: null, - tileData: null, // API tile metadata - imagesLoaded: false, // JPG/PNG loaded on map - moundLoaded: false, // .mound data in cache - apiError: false // Failed to fetch tile info -}); +const contextMenuRef = ref(null); const popup = ref({ visible: false, @@ -220,31 +198,6 @@ const highlightedCitation = ref(null); let map = null; let drawingHandler = null; -// ============================================================================ -// COORDINATE UTILITIES -// ============================================================================ - -function webMercatorToLonLat(x, y) { - const R = 6378137; - const lon = (x / R) * (180 / Math.PI); - const lat = (2 * Math.atan(Math.exp(y / R)) - Math.PI / 2) * (180 / Math.PI); - return [lon, lat]; -} - -// ============================================================================ -// TILE UTILITIES -// ============================================================================ - -// Check if tile images are loaded on map -function areTileImagesLoaded(tileId) { - return tilesStore.areImagesOnMap(tileId); -} - -// Check if mound data is cached -function isMoundDataCached(tileId) { - return tilesStore.hasMoundData(tileId); -} - // ============================================================================ // FILE PARSING // ============================================================================ @@ -296,13 +249,13 @@ function parseMoundBuffer(buffer) { async function loadTileImages(tileId, tileMetadata, imageUrl = null) { // If no explicit imageUrl provided (loading from server), skip if already loaded - if (!imageUrl && areTileImagesLoaded(tileId)) { + if (!imageUrl && tilesStore.areImagesOnMap(tileId)) { console.log(`Images for ${tileId} already loaded`); return; } // If imageUrl is provided (sandbox render), allow overwriting - const isOverwriting = imageUrl && areTileImagesLoaded(tileId); + const isOverwriting = imageUrl && tilesStore.areImagesOnMap(tileId); try { // Determine which format to load (prefer PNG over JPG) @@ -381,157 +334,46 @@ async function loadInitialTiles() { console.log(`Loaded ${tilesStore.getAllTileIdsWithImages.length} initial tiles`); } -async function loadMoundData(tileId) { - if (isMoundDataCached(tileId)) { - console.log(`Mound data for ${tileId} already cached`); - return; - } - - try { - await tilesStore.fetchMoundData(tileId, parseMoundBuffer); - console.log(`Loaded mound data for ${tileId}`); - } catch (err) { - console.error(`Failed to load mound data for ${tileId}:`, err); - throw err; - } -} - // ============================================================================ -// CONTEXT MENU HANDLERS +// CONTEXT MENU HANDLER // ============================================================================ async function handleContextMenu(e) { e.preventDefault(); - const lng = e.lngLat.lng; - const lat = e.lngLat.lat; - - // Reset context menu state - contextMenu.value = { - visible: false, - x: e.point.x, - y: e.point.y, - lngLat: { lng, lat }, - tileData: null, - imagesLoaded: false, - moundLoaded: false, - apiError: false - }; - - // Try to fetch tile metadata from API - try { - const tileData = await tilesStore.fetchMetadataByCoords(lat, lng); - - if (tileData) { - contextMenu.value.tileData = tileData; - contextMenu.value.imagesLoaded = areTileImagesLoaded(tileData.id); - contextMenu.value.moundLoaded = isMoundDataCached(tileData.id); - } - } catch (err) { - console.error('Failed to fetch tile info:', err); - contextMenu.value.apiError = true; + if (contextMenuRef.value) { + contextMenuRef.value.show(e.point.x, e.point.y, { + lng: e.lngLat.lng, + lat: e.lngLat.lat + }); } - - // Show context menu - contextMenu.value.visible = true; } -async function requestTileFromContextMenu() { - const { lng, lat } = contextMenu.value.lngLat; - contextMenu.value.visible = false; - - const requestId = `req-${nextRequestId++}`; - - // Initialize request tracking - tileRequests.value[requestId] = { - lat, - lng, - status: 'looking_up', - message: null, - tileId: null - }; - - // Start SSE request - tilesStore.requestTileProcessing( - lat, - lng, - async (data) => { - // Update request status - tileRequests.value[requestId].status = data.status; - tileRequests.value[requestId].message = data.message || null; - - if (data.tile_id) { - tileRequests.value[requestId].tileId = data.tile_id; - } - - // On ready: load mound and open sandbox - if (data.status === 'ready' && data.tile_id) { - try { - await loadMoundData(data.tile_id); - await openSandboxWithTile(data.tile_id); - } catch (err) { - console.error('Failed to load mound after request:', err); - tileRequests.value[requestId].status = 'error'; - tileRequests.value[requestId].message = 'Failed to load data'; - } - } - - // Auto-dismiss after 10 seconds if complete - if (data.status === 'ready' || data.status === 'error') { - setTimeout(() => { - delete tileRequests.value[requestId]; - }, 10000); - } - }, - (error) => { - console.error('Request tile error:', error); - tileRequests.value[requestId].status = 'error'; - tileRequests.value[requestId].message = 'Connection failed'; - - setTimeout(() => { - delete tileRequests.value[requestId]; - }, 10000); - } - ); -} +// ============================================================================ +// CONTEXT MENU - MAP OPERATION HANDLERS +// ============================================================================ -async function loadImagesFromContextMenu() { - const tileData = contextMenu.value.tileData; - contextMenu.value.visible = false; - - if (tileData) { +async function handleAddImagesToMap(tileId) { + const tileMetadata = tilesStore.getMetadata(tileId); + if (tileMetadata) { try { - await loadTileImages(tileData.id, tileData); + await loadTileImages(tileId, tileMetadata); } catch (err) { console.error('Failed to load images:', err); } } } -async function loadMoundFromContextMenu() { - const tileData = contextMenu.value.tileData; - contextMenu.value.visible = false; - - if (tileData) { - try { - await loadMoundData(tileData.id); - } catch (err) { - console.error('Failed to load mound data:', err); - } - } +function handleOpenSandbox(tileId) { + openSandboxWithTile(tileId); } -async function openSandboxFromContextMenu() { - const tileData = contextMenu.value.tileData; - contextMenu.value.visible = false; - - if (tileData) { - await openSandboxWithTile(tileData.id); - } +function handleDropPin(lngLat) { + dropPin(lngLat); } -function dismissTileRequest(requestId) { - delete tileRequests.value[requestId]; +function handleStartMeasure(lngLat) { + startMeasure(lngLat); } // ============================================================================ @@ -657,10 +499,6 @@ function openSandbox() { } async function openSandboxWithTile(tileId) { - if (!isMoundDataCached(tileId)) { - await loadMoundData(tileId); - } - sandboxVisible.value = true; setTimeout(() => { @@ -714,7 +552,8 @@ function setDrawMode(mode) { } drawingHandler = (e) => { - if (contextMenu.value.visible) return; + // Don't draw if context menu is visible + if (contextMenuRef.value?.visible) return; drawPoints.value.push([e.lngLat.lng, e.lngLat.lat]); @@ -758,8 +597,8 @@ function completeDrawing() { setDrawMode(null); } -function dropPin() { - const { lng, lat } = contextMenu.value.lngLat; +function dropPin(lngLat) { + const { lng, lat } = lngLat; const feature = { type: 'Feature', @@ -777,13 +616,11 @@ function dropPin() { geometryFeatures.value.features.push(feature); updateGeometryLayer(); saveGeometry(); - contextMenu.value.visible = false; } -function startMeasure() { - contextMenu.value.visible = false; +function startMeasure(lngLat) { setDrawMode('line'); - const { lng, lat } = contextMenu.value.lngLat; + const { lng, lat } = lngLat; drawPoints.value = [[lng, lat]]; } @@ -882,7 +719,8 @@ onMounted(() => { type: 'raster', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], tileSize: 256, - attribution: '© OpenStreetMap contributors' + attribution: '© OpenStreetMap contributors', + maxZoom: 20 }, satellite: { type: 'raster', @@ -890,7 +728,8 @@ onMounted(() => { 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}' ], tileSize: 256, - attribution: '© Esri' + attribution: '© Esri', + maxZoom: 21 } }, layers: [ @@ -945,12 +784,16 @@ onMounted(() => { popup.value.visible = false; } - contextMenu.value.visible = false; + if (contextMenuRef.value) { + contextMenuRef.value.hide(); + } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { - contextMenu.value.visible = false; + if (contextMenuRef.value) { + contextMenuRef.value.hide(); + } popup.value.visible = false; bibliographyVisible.value = false; if (drawMode.value) { diff --git a/ui/src/components/ContextMenu.vue b/ui/src/components/ContextMenu.vue index f3a5f96..c24102b 100644 --- a/ui/src/components/ContextMenu.vue +++ b/ui/src/components/ContextMenu.vue @@ -9,8 +9,9 @@
{{ formatCoordinate(lngLat.lng, 'lng') }}, {{ formatCoordinate(lngLat.lat, 'lat') }} - {{ tileData.id }} - ⚠️ Lookup failed + {{ tileMetadata.id }} + ⚠️ Lookup failed + {{ processingStatus }}
@@ -18,40 +19,47 @@ - - + + + +
+ ⏳ {{ processingStatus || 'Requesting tile...' }} +
+