Skip to content

Commit 42b1963

Browse files
committed
Zoom out when enabling the download grid, rename fieldBoundaryOpacity to opacity
1 parent 48374cb commit 42b1963

7 files changed

Lines changed: 61 additions & 22 deletions

File tree

src/components/GlobalDataPanel.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ const handleLocationSelected = (place: PlaceResult) => {
5454
thumb-color="teal"
5555
hide-details
5656
/>
57-
<h3 class="group">Opacity: {{ settings.fieldBoundariesOpacity }}%</h3>
57+
<h3 class="group">Opacity: {{ settings.opacity }}%</h3>
5858
<v-slider
59-
v-model.number="settings.fieldBoundariesOpacity"
59+
v-model.number="settings.opacity"
6060
:min="0"
6161
:max="100"
6262
:step="1"

src/composables/__tests__/useDownloadGrid.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,27 @@ describe('useDownloadGrid', () => {
171171
})
172172
expect(settings.value.downloads).toBe(true)
173173
})
174+
175+
it('animates zoom out to level 8 when downloads are enabled at a higher zoom', async () => {
176+
const { settings } = useSettings()
177+
const { initDownloadGridLayer } = useDownloadGrid()
178+
const animate = vi.fn()
179+
const fakeMap = {
180+
addLayer: vi.fn(),
181+
on: vi.fn(),
182+
getView: () => ({
183+
getZoom: () => 12,
184+
animate,
185+
}),
186+
getTargetElement: () => ({ style: { cursor: '' } }),
187+
} as any
188+
189+
settings.value.downloads = false
190+
initDownloadGridLayer(fakeMap)
191+
192+
settings.value.downloads = true
193+
await nextTick()
194+
195+
expect(animate).toHaveBeenCalledWith({ zoom: 8, duration: 500 })
196+
})
174197
})

src/composables/__tests__/usePermalink.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('buildGlobalPermalinkParts', () => {
1919
mode: 'global',
2020
threshold: 0.65,
2121
year: 2024,
22-
fieldBoundariesOpacity: 55,
22+
opacity: 55,
2323
downloads: true,
2424
})
2525

@@ -42,7 +42,7 @@ describe('parsePermalinkHash', () => {
4242
expect(state.mode).toBe('global')
4343
expect(state.threshold).toBe(0.8)
4444
expect(state.year).toBe(2025)
45-
expect(state.fieldBoundariesOpacity).toBe(72)
45+
expect(state.opacity).toBe(72)
4646
expect(state.downloads).toBe(true)
4747
})
4848

src/composables/useDownloadGrid.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ const selectedGridCell = ref<GridCell | null>(null)
2424
const hoveredGridFeature = shallowRef<FeatureLike | null>(null)
2525
const selectedGridFeature = shallowRef<FeatureLike | null>(null)
2626
const downloadGridLayer = shallowRef<VectorLayer<VectorSource> | null>(null)
27+
const currentMap = shallowRef<Map | null>(null)
28+
29+
const DOWNLOAD_GRID_MAX_ZOOM = 8
30+
const DOWNLOAD_GRID_ZOOM_ANIMATION_DURATION = 500
2731

2832
// Track maps we've already wired up so we don't register duplicate listeners.
2933
const initializedMaps = new WeakSet<Map>()
@@ -48,11 +52,25 @@ function closeDownloadModal() {
4852
downloadGridLayer.value?.changed()
4953
}
5054

55+
function ensureDownloadGridVisibleAtUsableZoom(map: Map | null) {
56+
if (!map || !settings.value.downloads) return
57+
58+
const view = map.getView()
59+
const zoom = view.getZoom()
60+
if (zoom !== undefined && zoom > DOWNLOAD_GRID_MAX_ZOOM) {
61+
view.animate({
62+
zoom: DOWNLOAD_GRID_MAX_ZOOM,
63+
duration: DOWNLOAD_GRID_ZOOM_ANIMATION_DURATION,
64+
})
65+
}
66+
}
67+
5168
// Toggle layer visibility and close the modal when the grid is turned off.
5269
watch(
5370
() => settings.value.downloads,
5471
(visible) => {
5572
downloadGridLayer.value?.setVisible(visible)
73+
if (visible) ensureDownloadGridVisibleAtUsableZoom(currentMap.value)
5674
if (!visible) closeDownloadModal()
5775
},
5876
)
@@ -70,11 +88,13 @@ export default function useDownloadGrid() {
7088
const initDownloadGridLayer = (map: Map) => {
7189
if (initializedMaps.has(map)) return
7290
initializedMaps.add(map)
91+
currentMap.value = map
7392

7493
const layer = createDownloadGridLayer(hoveredGridFeature, selectedGridFeature)
7594
downloadGridLayer.value = layer
7695
map.addLayer(layer)
7796
layer.setVisible(settings.value.downloads)
97+
ensureDownloadGridVisibleAtUsableZoom(map)
7898

7999
map.on('pointermove', (event) => {
80100
if (!settings.value.downloads) return

src/composables/useMap.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,14 @@ watch(
9494
}
9595

9696
globalPredictionsController.value = createGlobalPredictionsLayer(settings.value)
97-
globalPredictionsController.value.layer.setOpacity(
98-
settings.value.fieldBoundariesOpacity / 100,
99-
)
97+
globalPredictionsController.value.layer.setOpacity(settings.value.opacity / 100)
10098
map.value.addLayer(globalPredictionsController.value.layer)
10199
}
102100
},
103101
)
104102

105103
watch(
106-
() => settings.value.fieldBoundariesOpacity,
104+
() => settings.value.opacity,
107105
(opacity) => {
108106
const olOpacity = opacity / 100
109107
globalPredictionsController.value?.layer.setOpacity(olOpacity)
@@ -162,14 +160,12 @@ const updateLayers = () => {
162160
if (!globalPredictionsController.value) {
163161
// Only handle first initialization here, year changes are handled by a watcher on year above
164162
globalPredictionsController.value = createGlobalPredictionsLayer(settings.value)
165-
globalPredictionsController.value.layer.setOpacity(
166-
settings.value.fieldBoundariesOpacity / 100,
167-
)
163+
globalPredictionsController.value.layer.setOpacity(settings.value.opacity / 100)
168164
map.value.addLayer(globalPredictionsController.value.layer)
169165
}
170166
if (!globalOverviewLayer.value) {
171167
globalOverviewLayer.value = createGlobalOverviewLayer(settings.value)
172-
globalOverviewLayer.value.setOpacity(settings.value.fieldBoundariesOpacity / 100)
168+
globalOverviewLayer.value.setOpacity(settings.value.opacity / 100)
173169
map.value.addLayer(globalOverviewLayer.value)
174170
}
175171

src/composables/usePermalink.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface PermalinkStateInference extends PermalinkState {
3030

3131
export interface PermalinkStateGlobal extends PermalinkState {
3232
threshold: number
33-
fieldBoundariesOpacity: number
33+
opacity: number
3434
downloads: boolean
3535
}
3636

@@ -39,7 +39,7 @@ export function buildGlobalPermalinkParts(settings: Settings): string[] {
3939
if (settings.year) {
4040
hashParts.push(`year:${settings.year}`)
4141
}
42-
hashParts.push(`opacity:${settings.fieldBoundariesOpacity}`)
42+
hashParts.push(`opacity:${settings.opacity}`)
4343
hashParts.push(`downloads:${settings.downloads ? 1 : 0}`)
4444
return hashParts
4545
}
@@ -64,7 +64,7 @@ export function parsePermalinkHash(
6464
center: [0, 0],
6565
year: 2025,
6666
threshold: 0.4,
67-
fieldBoundariesOpacity: 90,
67+
opacity: 90,
6868
downloads: false,
6969
}
7070

@@ -169,7 +169,7 @@ export function parsePermalinkHash(
169169
zoom,
170170
center,
171171
threshold: defaultGlobalState.threshold,
172-
fieldBoundariesOpacity: defaultGlobalState.fieldBoundariesOpacity,
172+
opacity: defaultGlobalState.opacity,
173173
downloads: defaultGlobalState.downloads,
174174
}
175175

@@ -182,7 +182,7 @@ export function parsePermalinkHash(
182182
if (!isNaN(year)) result.year = year
183183
} else if (part.startsWith('opacity:')) {
184184
const opacity = parseInt(part.substring(8), 10)
185-
result.fieldBoundariesOpacity = isNaN(opacity) ? 90 : opacity
185+
result.opacity = isNaN(opacity) ? 90 : opacity
186186
} else if (part.startsWith('downloads:')) {
187187
result.downloads = part.substring(10) === '1'
188188
}
@@ -313,7 +313,7 @@ export default function usePermalink() {
313313
center,
314314
threshold: settings.value.threshold,
315315
year: settings.value.year,
316-
fieldBoundariesOpacity: settings.value.fieldBoundariesOpacity,
316+
opacity: settings.value.opacity,
317317
downloads: settings.value.downloads,
318318
}
319319
}
@@ -351,7 +351,7 @@ export default function usePermalink() {
351351

352352
function restoreGlobalState(state: PermalinkStateGlobal) {
353353
settings.value.threshold = state.threshold
354-
settings.value.fieldBoundariesOpacity = state.fieldBoundariesOpacity
354+
settings.value.opacity = state.opacity
355355
settings.value.downloads = state.downloads
356356
if (state.year) {
357357
settings.value.year = state.year
@@ -384,7 +384,7 @@ export default function usePermalink() {
384384
settings.value.areaCoverage,
385385
settings.value.buffer,
386386
settings.value.threshold,
387-
settings.value.fieldBoundariesOpacity,
387+
settings.value.opacity,
388388
settings.value.downloads,
389389
],
390390
() => {

src/composables/useSettings.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface Settings {
1212
expertMode: boolean
1313
mode: string
1414
threshold: number
15-
fieldBoundariesOpacity: number
15+
opacity: number
1616
downloads: boolean
1717
}
1818

@@ -59,7 +59,7 @@ const defaultSettings: Settings = {
5959
expertMode: false,
6060
mode: defaultMode,
6161
threshold: 0.4,
62-
fieldBoundariesOpacity: 90,
62+
opacity: 90,
6363
downloads: false,
6464
}
6565

0 commit comments

Comments
 (0)