Skip to content

Commit 1578bfe

Browse files
authored
Hotfixes (#270)
* Use different PMTiles files, change basemap info * Add year to feedback requests * Fix year swapping issue * Unify tooltips * Show tooltip for download tiles
2 parents 5c3c799 + 19afe0f commit 1578bfe

8 files changed

Lines changed: 191 additions & 66 deletions

File tree

src/components/GlobalDataPanel.vue

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,17 @@ const handleLocationSelected = (place: PlaceResult) => {
6969
</v-radio-group>
7070
<v-alert
7171
v-if="basemapYearMismatch"
72-
type="warning"
72+
type="info"
7373
variant="tonal"
7474
density="compact"
75-
class="mt-2"
75+
class="mt-2 note"
7676
>
77-
No basemap is available for {{ settings.year }}. Showing the
78-
{{ getEffectiveCloudlessYear(settings.year) }} basemap instead.
77+
Showing the {{ getEffectiveCloudlessYear(settings.year) }} basemap. The 2025 basemap will
78+
be added soon.
7979
</v-alert>
8080
<h3 class="group">
8181
Confidence Threshold: {{ settings.threshold }}%
82-
<v-tooltip max-width="360" location="top">
82+
<v-menu open-on-hover :close-on-content-click="false" max-width="360">
8383
<template #activator="{ props }">
8484
<v-icon
8585
class="ml-2"
@@ -88,12 +88,12 @@ const handleLocationSelected = (place: PlaceResult) => {
8888
v-bind="props"
8989
></v-icon>
9090
</template>
91-
<span
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
94-
ones.</span
95-
>
96-
</v-tooltip>
91+
<v-sheet class="pa-3 text-body-2">
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
94+
ones.
95+
</v-sheet>
96+
</v-menu>
9797
</h3>
9898
<v-slider
9999
v-model.number="settings.threshold"
@@ -146,7 +146,7 @@ const handleLocationSelected = (place: PlaceResult) => {
146146
/>
147147
<h3 class="group legend">
148148
Legend
149-
<v-tooltip max-width="360" location="top">
149+
<v-menu open-on-hover :close-on-content-click="false" max-width="360">
150150
<template #activator="{ props }">
151151
<v-icon
152152
class="ml-2"
@@ -155,11 +155,11 @@ const handleLocationSelected = (place: PlaceResult) => {
155155
v-bind="props"
156156
></v-icon>
157157
</template>
158-
<span
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>.</span
161-
>
162-
</v-tooltip>
158+
<v-sheet class="pa-3 text-body-2">
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>.
161+
</v-sheet>
162+
</v-menu>
163163
</h3>
164164
<MapLegend />
165165
</v-col>

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: 34 additions & 23 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-tooltip v-if="model.description" max-width="400" open-on-click>
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"
@@ -485,7 +490,7 @@ defineExpose({ openModelSelection })
485490
v-bind="props"
486491
></v-icon>
487492
</template>
488-
<div>
493+
<v-sheet class="pa-3 text-body-2">
489494
<template v-if="model.version"
490495
><strong>Version:</strong> {{ model.version }}<br
491496
/></template>
@@ -496,8 +501,8 @@ defineExpose({ openModelSelection })
496501
{{ model.description }}
497502
</div>
498503
</template>
499-
</div>
500-
</v-tooltip>
504+
</v-sheet>
505+
</v-menu>
501506
<v-badge
502507
v-if="model.legacy"
503508
inline
@@ -681,9 +686,15 @@ defineExpose({ openModelSelection })
681686

682687
<v-row v-if="basemapYearMismatch">
683688
<v-col>
684-
<v-alert type="warning" variant="tonal" density="compact">
685-
No basemap is available for {{ settings.year }}. Showing the
686-
{{ getEffectiveCloudlessYear(settings.year) }} basemap instead.
689+
<v-alert
690+
v-if="basemapYearMismatch"
691+
type="info"
692+
variant="tonal"
693+
density="compact"
694+
class="mt-2 note"
695+
>
696+
Showing the {{ getEffectiveCloudlessYear(settings.year) }} basemap. The 2025 basemap
697+
will be added soon.
687698
</v-alert>
688699
</v-col>
689700
</v-row>
@@ -944,12 +955,8 @@ defineExpose({ openModelSelection })
944955
></v-badge>
945956
</span>
946957
<v-spacer></v-spacer>
947-
<v-tooltip
948-
v-if="activeTileId"
949-
:text="stacPreviewTileId === activeTileId ? 'Hide scene A image' : 'Show scene A image'"
950-
open-on-click
951-
>
952-
<template v-slot:activator="{ props }">
958+
<v-menu v-if="activeTileId" open-on-hover :close-on-content-click="false">
959+
<template #activator="{ props }">
953960
<v-btn
954961
v-bind="props"
955962
:icon="stacPreviewTileId === activeTileId ? mdiEyeOutline : mdiEyeOffOutline"
@@ -959,7 +966,10 @@ defineExpose({ openModelSelection })
959966
"
960967
></v-btn>
961968
</template>
962-
</v-tooltip>
969+
<v-sheet class="pa-3 text-body-2">
970+
{{ stacPreviewTileId === activeTileId ? 'Hide scene A image' : 'Show scene A image' }}
971+
</v-sheet>
972+
</v-menu>
963973
</v-expansion-panel-title>
964974
<v-expansion-panel-text>
965975
<div class="results">
@@ -1034,14 +1044,8 @@ defineExpose({ openModelSelection })
10341044
></v-badge>
10351045
</span>
10361046
<v-spacer></v-spacer>
1037-
<v-tooltip
1038-
v-if="secondActiveTileId"
1039-
:text="
1040-
stacPreviewTileId === secondActiveTileId ? 'Hide scene B image' : 'Show scene B image'
1041-
"
1042-
open-on-click
1043-
>
1044-
<template v-slot:activator="{ props }">
1047+
<v-menu v-if="secondActiveTileId" open-on-hover :close-on-content-click="false">
1048+
<template #activator="{ props }">
10451049
<v-btn
10461050
v-bind="props"
10471051
:icon="stacPreviewTileId === secondActiveTileId ? mdiEyeOutline : mdiEyeOffOutline"
@@ -1052,7 +1056,14 @@ defineExpose({ openModelSelection })
10521056
"
10531057
></v-btn>
10541058
</template>
1055-
</v-tooltip>
1059+
<v-sheet class="pa-3 text-body-2">
1060+
{{
1061+
stacPreviewTileId === secondActiveTileId
1062+
? 'Hide scene B image'
1063+
: 'Show scene B image'
1064+
}}
1065+
</v-sheet>
1066+
</v-menu>
10561067
</v-expansion-panel-title>
10571068
<v-expansion-panel-text>
10581069
<div class="results">

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)