Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/components/DataCabinet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { mdiChevronDown } from '@mdi/js'
import { ref } from 'vue'
import useSearch from '../composables/useSearch'
import useSettings from '../composables/useSettings'
import useMap from '../composables/useMap'
import ProcessingPanel from './ProcessingPanel.vue'
import GlobalDataPanel from './GlobalDataPanel.vue'

const { searchStatus } = useSearch()
const { modelTitle, settings } = useSettings()
const { isLayerLoading } = useMap()

// Sidebar state
const isWorking = ref(false)
Expand All @@ -19,7 +21,7 @@ const toggleCollapsible = () => {

<template>
<v-card
:loading="isWorking || searchStatus === true"
:loading="isWorking || isLayerLoading || searchStatus === true"
elevation="8"
:class="{ closed: !isOpen, 'data-cabinet': true, sidebar: true, [settings.mode]: true }"
>
Expand Down
14 changes: 12 additions & 2 deletions src/components/GeocodingSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ const isLoadingPlaces = ref(false)
const placeSearch = ref('')
const suggestedPlaces = shallowRef<{ value: PlaceResult; title: string }[]>([])

// Set loading immediately so the indicator appears before the debounce delay
watch(placeSearch, (newSearch) => {
if (suggestedPlaces.value.some((p) => p.title === newSearch)) return
if (newSearch.length >= 3) {
isLoadingPlaces.value = true
} else {
isLoadingPlaces.value = false
suggestedPlaces.value = []
}
})

watch(
placeSearch,
debounce(async (newSearch: string) => {
if (newSearch.length < 3) return
if (suggestedPlaces.value.some((p) => p.title === newSearch) || newSearch.length < 3) return

isLoadingPlaces.value = true
try {
const referer = 'https://github.com/fieldsoftheworld/ftw-inference-app'
const response = await fetch(
Expand Down
43 changes: 42 additions & 1 deletion src/composables/useMap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ref, shallowRef, watch } from 'vue'
import { ref, shallowRef, watch, computed } from 'vue'
import type TileSource from 'ol/source/Tile'
import type Map from 'ol/Map'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
Expand Down Expand Up @@ -28,6 +29,24 @@ import { inferenceStyle } from '../layers/color-scales'

let featureId = 0

const loadingCount = ref(0)
export const isLayerLoading = computed(() => loadingCount.value > 0)

export function trackTileSource(source: TileSource): () => void {
const onStart = () => {
loadingCount.value++
}
const onEnd = () => {
loadingCount.value = Math.max(0, loadingCount.value - 1)
}
source.on('tileloadstart', onStart)
source.on(['tileloadend', 'tileloaderror'], onEnd)
return () => {
source.un('tileloadstart', onStart)
source.un(['tileloadend', 'tileloaderror'], onEnd)
}
}

export interface AreaValues {
min_area_km2: number
max_area_km2: number
Expand Down Expand Up @@ -56,6 +75,13 @@ export const geoJsonResults = shallowRef<any[]>([])
// Cloudless layer management
const cloudlessLayer = shallowRef<TileLayer<XYZ> | null>(null)

let untrackCloudless: (() => void) | null = null
watch(cloudlessLayer, (newLayer) => {
untrackCloudless?.()
const src = newLayer?.getSource() as TileSource | null
untrackCloudless = src ? trackTileSource(src) : null
})

// Watch for year changes and update the cloudless layer
watch(
() => settings.value.year,
Expand Down Expand Up @@ -99,6 +125,20 @@ const s2GridLayer = shallowRef<VectorLayer<VectorSource> | null>(null)
const globalPredictionsLayer = shallowRef<VectorTileLayer | null>(null)
const globalOverviewLayer = shallowRef<GlTileLayer | null>(null)

let untrackGlobalPredictions: (() => void) | null = null
watch(globalPredictionsLayer, (newLayer) => {
untrackGlobalPredictions?.()
const src = newLayer?.getSource() as TileSource | null
untrackGlobalPredictions = src ? trackTileSource(src) : null
})

let untrackGlobalOverview: (() => void) | null = null
watch(globalOverviewLayer, (newLayer) => {
untrackGlobalOverview?.()
const src = newLayer?.getSource() as TileSource | null
untrackGlobalOverview = src ? trackTileSource(src) : null
})

watch(
() => settings.value.threshold,
() => {
Expand Down Expand Up @@ -365,5 +405,6 @@ export default function useMap() {
geoJsonResults,
initCloudlessLayer,
updateLayers,
isLayerLoading,
}
}
9 changes: 8 additions & 1 deletion src/composables/useStacLayer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { shallowRef, watch } from 'vue'
import { ImageTile } from 'ol/source'
import TileLayer from 'ol/layer/Tile'
import { map } from './useMap'
import { map, trackTileSource } from './useMap'
import { getTileById, activeTileId, secondActiveTileId } from './useAreaOfInterest'
import { transformExtent } from 'ol/proj'
import useSettings from './useSettings'

let currentStacLayer: TileLayer<ImageTile> | null = null
let untrackStac: (() => void) | null = null
const stacPreviewTileId = shallowRef<string | null>(null)
/** Stores the preview tile id while in global mode so it can be restored */
let savedPreviewTileId: string | null = null
Expand Down Expand Up @@ -38,12 +39,18 @@ async function addStacLayer() {
})
// Add the new layer to the map
map.value.addLayer(currentStacLayer)
const src = currentStacLayer.getSource()
if (src) {
untrackStac = trackTileSource(src)
}
}

function removeStacLayer() {
if (!currentStacLayer) {
return
}
untrackStac?.()
untrackStac = null
map.value?.removeLayer(currentStacLayer)
currentStacLayer = null
}
Expand Down
Loading