Skip to content

Commit 5bf475c

Browse files
committed
Allow for underzoom on Overture layer.
1 parent 7d8085c commit 5bf475c

5 files changed

Lines changed: 75 additions & 4 deletions

File tree

.claude/skills/conflate-snapshots/SKILL.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ upload for web consumption.
112112

113113
## Next
114114

115-
- Bump the frontend: [skills/update-site](../update-site/SKILL.md).
115+
- **Always bump the frontend after a successful upload** — the site's PMTiles
116+
URLs are pinned to a version folder and do **not** auto-follow new uploads, so
117+
a refresh isn't complete until the frontend points at the new
118+
`versions.source_coop`. Treat this as a required step of every monthly refresh,
119+
not an optional follow-up: [skills/update-site](../update-site/SKILL.md).
116120

117121
## Key code
118122

site/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<link rel="icon" type="image/png" sizes="32x32" href="/src/assets/favicon-32x32.png" />
99
<link
1010
rel="stylesheet"
11-
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,700,0,0&icon_names=my_location"
11+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,700,0,0&icon_names=my_location,zoom_in"
1212
/>
1313
</head>
1414
<body>

site/src/assets/styles.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,31 @@ body,
325325
text-align: right;
326326
}
327327

328+
/* Overture "zoom in" hint (Overture tiles are z14-only) */
329+
.overture-zoom-hint {
330+
position: absolute;
331+
top: 50%;
332+
left: 50%;
333+
transform: translate(-50%, -50%);
334+
z-index: 100;
335+
display: flex;
336+
align-items: center;
337+
gap: 6px;
338+
background: rgba(255, 255, 255, 0.92);
339+
border: 1px solid #ccc;
340+
border-radius: 20px;
341+
padding: 8px 16px;
342+
font-size: 14px;
343+
font-weight: 500;
344+
color: #444;
345+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
346+
pointer-events: none;
347+
}
348+
349+
.overture-zoom-hint .material-symbols-outlined {
350+
font-size: 20px;
351+
}
352+
328353
/* Geolocation button */
329354
.geolocation-btn {
330355
position: absolute;

site/src/components/MapContainer.vue

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
v-if="props.activeSource === 'osm' || props.activeSource === 'overture' || props.activeSource === 'conflated'"
1515
/>
1616

17+
<!-- Overture's archive is z14-only; below the under-zoom floor we prompt
18+
the user to zoom in rather than showing an empty map. -->
19+
<div v-if="showOvertureZoomHint" class="overture-zoom-hint">
20+
<span class="material-symbols-outlined">zoom_in</span>
21+
Zoom in to see Overture POIs
22+
</div>
23+
1724
<!-- Desktop: native select -->
1825
<div class="basemap-switcher basemap-switcher-desktop">
1926
<select v-model="selectedStyle" @change="switchBaseMap">
@@ -60,7 +67,7 @@
6067

6168
<script setup>
6269
import {
63-
ref, shallowRef, watch, onMounted, onBeforeUnmount, nextTick,
70+
ref, shallowRef, computed, watch, onMounted, onBeforeUnmount, nextTick,
6471
} from 'vue'
6572
import Map from 'ol/Map'
6673
import View from 'ol/View'
@@ -80,6 +87,7 @@ import {
8087
getOvertureLayer,
8188
updateOvertureFilters,
8289
wrapOvertureFeature,
90+
OVERTURE_MIN_ZOOM,
8391
} from '../layers/overtureLayer.js'
8492
import {
8593
getConflatedLayer,
@@ -105,6 +113,13 @@ const popupEl = ref(null)
105113
const map = shallowRef(null)
106114
const popupOverlay = shallowRef(null)
107115
const selectedFeature = shallowRef(null)
116+
const currentZoom = ref(INITIAL_ZOOM)
117+
118+
// Overture is z14-only and only renders above OVERTURE_MIN_ZOOM (see
119+
// overtureLayer.js); below the floor, prompt the user to zoom in.
120+
const showOvertureZoomHint = computed(
121+
() => props.activeSource === 'overture' && currentZoom.value <= OVERTURE_MIN_ZOOM
122+
)
108123
const selectedStyle = ref('positron')
109124
const basemapModalOpen = ref(false)
110125
const baseMapStyles = BASE_MAP_STYLES
@@ -165,6 +180,10 @@ onMounted(async () => {
165180
166181
useMapHash(olMap)
167182
if (!hashState) handleGeolocate()
183+
184+
// Track view zoom so the Overture "zoom in" hint can react to it.
185+
currentZoom.value = view.getZoom()
186+
view.on('change:resolution', () => { currentZoom.value = view.getZoom() })
168187
})
169188
170189
onBeforeUnmount(() => {

site/src/layers/overtureLayer.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import VectorTileLayer from 'ol/layer/VectorTile'
22
import { PMTilesVectorSource } from 'ol-pmtiles'
3+
import { createXYZ } from 'ol/tilegrid'
4+
import { get as getProjection } from 'ol/proj'
35
import { Style, Circle, Fill, Stroke } from 'ol/style'
46
import {
57
confidenceColor,
@@ -14,14 +16,35 @@ let layer = null
1416
// Style cache keyed by conf bucket (same discretisation as OSM layer)
1517
const styleCache = {}
1618

19+
// Overture's hosted PMTiles archive contains tiles at z14 ONLY (unlike our
20+
// OSM/conflated archives, which carry a z10–z14 pyramid). OpenLayers does not
21+
// down-sample vector tiles, so without intervention Overture renders nothing
22+
// below z14. We pin the source tile grid to a single z14 level so OL reuses
23+
// the z14 tiles at lower view zooms (under-zoom), and floor rendering at
24+
// OVERTURE_MIN_ZOOM so we don't try to load a metro's worth of z14 tiles when
25+
// zoomed way out. Below the floor the UI shows a "zoom in" hint instead.
26+
export const OVERTURE_MIN_ZOOM = 13
27+
28+
const overtureTileGrid = createXYZ({
29+
extent: getProjection('EPSG:3857').getExtent(),
30+
minZoom: 14,
31+
maxZoom: 14,
32+
tileSize: 512,
33+
})
34+
1735
export function getOvertureLayer() {
1836
if (layer) return layer
1937

2038
layer = new VectorTileLayer({
21-
source: new PMTilesVectorSource({ url: OVERTURE_PMTILES_URL }),
39+
source: new PMTilesVectorSource({
40+
url: OVERTURE_PMTILES_URL,
41+
tileGrid: overtureTileGrid,
42+
}),
2243
style: overtureTileStyle,
2344
zIndex: 10,
2445
visible: false,
46+
// minZoom is exclusive: the layer renders only at view zoom > OVERTURE_MIN_ZOOM.
47+
minZoom: OVERTURE_MIN_ZOOM,
2548
})
2649
return layer
2750
}

0 commit comments

Comments
 (0)