Skip to content

Commit 10b9566

Browse files
authored
Loading indicator for OL layers / Nominatim (#248)
* Loading indicator for OL layers * Fix CI issues * Reduce number of Nominatim calls
1 parent b7cd3e0 commit 10b9566

4 files changed

Lines changed: 65 additions & 5 deletions

File tree

src/components/DataCabinet.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { mdiChevronDown } from '@mdi/js'
33
import { ref } from 'vue'
44
import useSearch from '../composables/useSearch'
55
import useSettings from '../composables/useSettings'
6+
import useMap from '../composables/useMap'
67
import ProcessingPanel from './ProcessingPanel.vue'
78
import GlobalDataPanel from './GlobalDataPanel.vue'
89
910
const { searchStatus } = useSearch()
1011
const { modelTitle, settings } = useSettings()
12+
const { isLayerLoading } = useMap()
1113
1214
// Sidebar state
1315
const isWorking = ref(false)
@@ -19,7 +21,7 @@ const toggleCollapsible = () => {
1921

2022
<template>
2123
<v-card
22-
:loading="isWorking || searchStatus === true"
24+
:loading="isWorking || isLayerLoading || searchStatus === true"
2325
elevation="8"
2426
:class="{ closed: !isOpen, 'data-cabinet': true, sidebar: true, [settings.mode]: true }"
2527
>

src/components/GeocodingSearch.vue

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,22 @@ const isLoadingPlaces = ref(false)
1212
const placeSearch = ref('')
1313
const suggestedPlaces = shallowRef<{ value: PlaceResult; title: string }[]>([])
1414
15+
// Set loading immediately so the indicator appears before the debounce delay
16+
watch(placeSearch, (newSearch) => {
17+
if (suggestedPlaces.value.some((p) => p.title === newSearch)) return
18+
if (newSearch.length >= 3) {
19+
isLoadingPlaces.value = true
20+
} else {
21+
isLoadingPlaces.value = false
22+
suggestedPlaces.value = []
23+
}
24+
})
25+
1526
watch(
1627
placeSearch,
1728
debounce(async (newSearch: string) => {
18-
if (newSearch.length < 3) return
29+
if (suggestedPlaces.value.some((p) => p.title === newSearch) || newSearch.length < 3) return
1930
20-
isLoadingPlaces.value = true
2131
try {
2232
const referer = 'https://github.com/fieldsoftheworld/ftw-inference-app'
2333
const response = await fetch(

src/composables/useMap.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { ref, shallowRef, watch } from 'vue'
1+
import { ref, shallowRef, watch, computed } from 'vue'
2+
import type TileSource from 'ol/source/Tile'
23
import type Map from 'ol/Map'
34
import VectorSource from 'ol/source/Vector'
45
import VectorLayer from 'ol/layer/Vector'
@@ -28,6 +29,24 @@ import { inferenceStyle } from '../layers/color-scales'
2829

2930
let featureId = 0
3031

32+
const loadingCount = ref(0)
33+
export const isLayerLoading = computed(() => loadingCount.value > 0)
34+
35+
export function trackTileSource(source: TileSource): () => void {
36+
const onStart = () => {
37+
loadingCount.value++
38+
}
39+
const onEnd = () => {
40+
loadingCount.value = Math.max(0, loadingCount.value - 1)
41+
}
42+
source.on('tileloadstart', onStart)
43+
source.on(['tileloadend', 'tileloaderror'], onEnd)
44+
return () => {
45+
source.un('tileloadstart', onStart)
46+
source.un(['tileloadend', 'tileloaderror'], onEnd)
47+
}
48+
}
49+
3150
export interface AreaValues {
3251
min_area_km2: number
3352
max_area_km2: number
@@ -56,6 +75,13 @@ export const geoJsonResults = shallowRef<any[]>([])
5675
// Cloudless layer management
5776
const cloudlessLayer = shallowRef<TileLayer<XYZ> | null>(null)
5877

78+
let untrackCloudless: (() => void) | null = null
79+
watch(cloudlessLayer, (newLayer) => {
80+
untrackCloudless?.()
81+
const src = newLayer?.getSource() as TileSource | null
82+
untrackCloudless = src ? trackTileSource(src) : null
83+
})
84+
5985
// Watch for year changes and update the cloudless layer
6086
watch(
6187
() => settings.value.year,
@@ -99,6 +125,20 @@ const s2GridLayer = shallowRef<VectorLayer<VectorSource> | null>(null)
99125
const globalPredictionsLayer = shallowRef<VectorTileLayer | null>(null)
100126
const globalOverviewLayer = shallowRef<GlTileLayer | null>(null)
101127

128+
let untrackGlobalPredictions: (() => void) | null = null
129+
watch(globalPredictionsLayer, (newLayer) => {
130+
untrackGlobalPredictions?.()
131+
const src = newLayer?.getSource() as TileSource | null
132+
untrackGlobalPredictions = src ? trackTileSource(src) : null
133+
})
134+
135+
let untrackGlobalOverview: (() => void) | null = null
136+
watch(globalOverviewLayer, (newLayer) => {
137+
untrackGlobalOverview?.()
138+
const src = newLayer?.getSource() as TileSource | null
139+
untrackGlobalOverview = src ? trackTileSource(src) : null
140+
})
141+
102142
watch(
103143
() => settings.value.threshold,
104144
() => {
@@ -365,5 +405,6 @@ export default function useMap() {
365405
geoJsonResults,
366406
initCloudlessLayer,
367407
updateLayers,
408+
isLayerLoading,
368409
}
369410
}

src/composables/useStacLayer.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { shallowRef, watch } from 'vue'
22
import { ImageTile } from 'ol/source'
33
import TileLayer from 'ol/layer/Tile'
4-
import { map } from './useMap'
4+
import { map, trackTileSource } from './useMap'
55
import { getTileById, activeTileId, secondActiveTileId } from './useAreaOfInterest'
66
import { transformExtent } from 'ol/proj'
77
import useSettings from './useSettings'
88

99
let currentStacLayer: TileLayer<ImageTile> | null = null
10+
let untrackStac: (() => void) | null = null
1011
const stacPreviewTileId = shallowRef<string | null>(null)
1112
/** Stores the preview tile id while in global mode so it can be restored */
1213
let savedPreviewTileId: string | null = null
@@ -38,12 +39,18 @@ async function addStacLayer() {
3839
})
3940
// Add the new layer to the map
4041
map.value.addLayer(currentStacLayer)
42+
const src = currentStacLayer.getSource()
43+
if (src) {
44+
untrackStac = trackTileSource(src)
45+
}
4146
}
4247

4348
function removeStacLayer() {
4449
if (!currentStacLayer) {
4550
return
4651
}
52+
untrackStac?.()
53+
untrackStac = null
4754
map.value?.removeLayer(currentStacLayer)
4855
currentStacLayer = null
4956
}

0 commit comments

Comments
 (0)