Skip to content

Commit 19afe0f

Browse files
committed
Show tooltip for download tiles
1 parent 536ee95 commit 19afe0f

4 files changed

Lines changed: 127 additions & 8 deletions

File tree

src/components/GlobalDataPanel.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ const handleLocationSelected = (place: PlaceResult) => {
8989
></v-icon>
9090
</template>
9191
<v-sheet class="pa-3 text-body-2">
92-
Only show fields where the model's confidence meets this threshold. Higher values
93-
show fewer, more certain fields; lower values show more fields, including less certain
92+
Only show fields where the model's confidence meets this threshold. Higher values show
93+
fewer, more certain fields; lower values show more fields, including less certain
9494
ones.
9595
</v-sheet>
9696
</v-menu>
@@ -156,8 +156,8 @@ const handleLocationSelected = (place: PlaceResult) => {
156156
></v-icon>
157157
</template>
158158
<v-sheet class="pa-3 text-body-2">
159-
Zoomed out, the map shows <strong>field density</strong> (fields per area). Zoom in
160-
to see individual fields colored by the model's <strong>confidence</strong>.
159+
Zoomed out, the map shows <strong>field density</strong> (fields per area). Zoom in to
160+
see individual fields colored by the model's <strong>confidence</strong>.
161161
</v-sheet>
162162
</v-menu>
163163
</h3>

src/components/MapComponent.vue

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,23 @@ const {
2929
updateLayers,
3030
} = useMap()
3131
32-
useDownloadGrid()
32+
const { hoveredGridCell, hoveredGridPosition } = useDownloadGrid()
3333
3434
const { addMapClickHandler } = useAreaOfInterest()
3535
const { setAvailableModels, settings } = useSettings()
3636
37+
function formatDeg(value: number, isLat: boolean): string {
38+
const abs = Math.abs(value)
39+
const dir = isLat ? (value >= 0 ? 'N' : 'S') : value >= 0 ? 'E' : 'W'
40+
return `${abs}°${dir}`
41+
}
42+
43+
function formatBytes(bytes: number): string {
44+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
45+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`
46+
return `${bytes} B`
47+
}
48+
3749
const clearResults = () => {
3850
geoJsonResults.value = []
3951
hidePropertiesBox()
@@ -121,6 +133,50 @@ defineExpose({
121133
/>
122134

123135
<GlobalFeedbackWidget v-if="map && settings.mode === 'global'" />
136+
137+
<!-- Download grid hover tooltip -->
138+
<div
139+
v-if="hoveredGridCell && hoveredGridPosition && settings.downloads"
140+
class="download-grid-tooltip"
141+
:style="{ left: hoveredGridPosition.x + 16 + 'px', top: hoveredGridPosition.y + 16 + 'px' }"
142+
>
143+
<div class="download-grid-tooltip__title">
144+
{{ settings.year }}_{{ hoveredGridCell.tile_id }}.parquet
145+
</div>
146+
<div class="download-grid-tooltip__row">
147+
<span class="download-grid-tooltip__label">Year</span>
148+
{{ settings.year }}
149+
</div>
150+
<div class="download-grid-tooltip__row">
151+
<span class="download-grid-tooltip__label">Lat</span>
152+
{{ formatDeg(hoveredGridCell.lat_min, true) }} –
153+
{{ formatDeg(hoveredGridCell.lat_min + 1, true) }}
154+
</div>
155+
<div class="download-grid-tooltip__row">
156+
<span class="download-grid-tooltip__label">Lon</span>
157+
{{ formatDeg(hoveredGridCell.lon_min, false) }} –
158+
{{ formatDeg(hoveredGridCell.lon_min + 1, false) }}
159+
</div>
160+
<template v-if="hoveredGridCell.years.includes(settings.year)">
161+
<div
162+
v-if="hoveredGridCell.feature_counts?.[String(settings.year)] != null"
163+
class="download-grid-tooltip__row"
164+
>
165+
<span class="download-grid-tooltip__label">Fields</span>
166+
{{ hoveredGridCell.feature_counts![String(settings.year)].toLocaleString() }}
167+
</div>
168+
<div
169+
v-if="hoveredGridCell.size_bytes?.[String(settings.year)] != null"
170+
class="download-grid-tooltip__row"
171+
>
172+
<span class="download-grid-tooltip__label">Download size</span>
173+
{{ formatBytes(hoveredGridCell.size_bytes![String(settings.year)]) }}
174+
</div>
175+
</template>
176+
<div v-else class="download-grid-tooltip__unavailable">
177+
Not available for {{ settings.year }}
178+
</div>
179+
</div>
124180
</div>
125181

126182
<!-- Properties Box -->
@@ -195,6 +251,47 @@ defineExpose({
195251
height: 100%;
196252
}
197253
254+
/* Download Grid Tooltip */
255+
.download-grid-tooltip {
256+
position: absolute;
257+
z-index: 9000;
258+
pointer-events: none;
259+
background-color: rgba(0, 0, 0, 0.9);
260+
border: 1px solid rgba(0, 136, 136, 0.7);
261+
border-radius: 6px;
262+
padding: 0.5rem 0.75rem;
263+
min-width: 180px;
264+
font-size: 0.8rem;
265+
color: #eee;
266+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
267+
backdrop-filter: blur(6px);
268+
}
269+
270+
.download-grid-tooltip__title {
271+
font-weight: 600;
272+
color: rgb(0, 200, 200);
273+
margin-bottom: 0.35rem;
274+
font-size: 0.85rem;
275+
}
276+
277+
.download-grid-tooltip__row {
278+
display: flex;
279+
justify-content: space-between;
280+
gap: 0.75rem;
281+
line-height: 1.6;
282+
}
283+
284+
.download-grid-tooltip__label {
285+
color: #aaa;
286+
white-space: nowrap;
287+
}
288+
289+
.download-grid-tooltip__unavailable {
290+
color: #f87171;
291+
margin-top: 0.25rem;
292+
font-size: 0.75rem;
293+
}
294+
198295
/* Properties Box Styles */
199296
.properties-box {
200297
position: fixed;

src/components/ProcessingPanel.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,12 @@ defineExpose({ openModelSelection })
476476
<v-radio v-for="model in availableModels" :key="model.id" :value="model.id" color="teal"
477477
><template v-slot:label>
478478
{{ model.title }}
479-
<v-menu v-if="model.description" open-on-hover :close-on-content-click="false" max-width="400">
479+
<v-menu
480+
v-if="model.description"
481+
open-on-hover
482+
:close-on-content-click="false"
483+
max-width="400"
484+
>
480485
<template #activator="{ props }">
481486
<v-icon
482487
class="ml-1"
@@ -1052,7 +1057,11 @@ defineExpose({ openModelSelection })
10521057
></v-btn>
10531058
</template>
10541059
<v-sheet class="pa-3 text-body-2">
1055-
{{ stacPreviewTileId === secondActiveTileId ? 'Hide scene B image' : 'Show scene B image' }}
1060+
{{
1061+
stacPreviewTileId === secondActiveTileId
1062+
? 'Hide scene B image'
1063+
: 'Show scene B image'
1064+
}}
10561065
</v-sheet>
10571066
</v-menu>
10581067
</v-expansion-panel-title>

src/composables/useDownloadGrid.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { shallowRef, watch } from 'vue'
1+
import { computed, shallowRef, watch } from 'vue'
22
import type Map from 'ol/Map'
33
import type VectorLayer from 'ol/layer/Vector'
44
import type VectorSource from 'ol/source/Vector'
@@ -21,6 +21,7 @@ const { settings } = useSettings()
2121

2222
// Singleton state shared by all consumers of the composable.
2323
const hoveredGridFeature = shallowRef<FeatureLike | null>(null)
24+
const hoveredGridPosition = shallowRef<{ x: number; y: number } | null>(null)
2425
const downloadGridLayer = shallowRef<VectorLayer<VectorSource> | null>(null)
2526
const currentMap = shallowRef<Map | null>(null)
2627

@@ -85,6 +86,7 @@ watch(
8586
ensureDownloadGridVisibleAtUsableZoom(currentMap.value)
8687
} else {
8788
hoveredGridFeature.value = null
89+
hoveredGridPosition.value = null
8890
downloadGridLayer.value?.changed()
8991
const el = currentMap.value?.getTargetElement()
9092
if (el) el.style.cursor = ''
@@ -101,6 +103,10 @@ watch(
101103
)
102104

103105
export default function useDownloadGrid() {
106+
const hoveredGridCell = computed(() =>
107+
hoveredGridFeature.value ? featureToGridCell(hoveredGridFeature.value) : null,
108+
)
109+
104110
/** Idempotent: creates the layer and registers listeners on first call per map. */
105111
const initDownloadGridLayer = (map: Map) => {
106112
if (initializedMaps.has(map)) return
@@ -124,6 +130,11 @@ export default function useDownloadGrid() {
124130
layer.changed()
125131
map.getTargetElement().style.cursor = feature ? 'pointer' : ''
126132
}
133+
if (feature) {
134+
hoveredGridPosition.value = { x: event.pixel[0], y: event.pixel[1] }
135+
} else {
136+
hoveredGridPosition.value = null
137+
}
127138
})
128139
}
129140

@@ -149,6 +160,8 @@ export default function useDownloadGrid() {
149160

150161
return {
151162
downloadGridLayer,
163+
hoveredGridCell,
164+
hoveredGridPosition,
152165
initDownloadGridLayer,
153166
handleGridClick,
154167
}

0 commit comments

Comments
 (0)