Home / Blog / The zoom level for Burgundy is 8. For Volnay it's 13.
May 19, 2026· Wine World Map
The zoom level for Burgundy is 8. For Volnay it's 13.
Click Burgundy and the map should show you Burgundy — all of it,
from Chablis in the north down through the Côte d'Or to the Mâconnais.
Click Volnay and you want to see Volnay's couple of square kilometres,
not the whole of France. Both clicks call the same flyTo function.
What changes is the zoom argument.
For the regions and districts that have polygons (see the OSM hull
post) the answer is easy: Maplibre's
fitBounds takes the bounding box and works out the camera. The
interesting case is the ~40% of districts where we don't have a polygon,
only a curated centroid. There you have to guess.
The first heuristic
The first version was a lookup on the appellation_type column:
const ZOOM_BY_TYPE: Record<AppellationType, number> = {
AVA: 10, // most AVAs are county-sized
AOC: 11, // most French AOCs are commune-sized
DOCG: 11,
DOC: 10,
DO: 10,
DOCa: 10,
IGP: 9, // typically broader
Country: 5,
Region: 8,
}
This is wrong in a charming variety of ways. Paso Robles AVA is 20× the area of Saint-Émilion AOC. Sicilia DOC is the entire island. Champagne AOC covers five departments. Pauillac AOC is six square kilometres. The classification tells you nothing about the size.
The second heuristic
When we have a bounding box — from a stored polygon, or from the convex hull of the winery points inside the region — degree-span maps to zoom pretty cleanly:
function zoomForBbox(bbox: [number, number, number, number]): number {
const [west, south, east, north] = bbox
const midLat = (north + south) / 2
const latSpan = north - south
const lngSpan = (east - west) * Math.cos(midLat * Math.PI / 180)
const span = Math.max(latSpan, lngSpan)
if (span > 8) return 6
if (span > 4) return 7
if (span > 2) return 8
if (span > 1) return 9
if (span > 0.5) return 10
if (span > 0.25) return 11
if (span > 0.1) return 12
return 13
}
The cos(midLat) correction matters more than you'd think. A
one-degree longitude span at Mendoza's latitude is about 96 km;
the same span in Mosel is 67 km. Without the correction the
Mosel valley gets the same zoom as a region twice its size.
Measured against real regions
| Region | bbox span (°) | computed zoom | feels right? | |---|---:|---:|---| | Bordeaux | 1.5 | 8 | yes | | Médoc | 0.4 | 11 | yes | | Pauillac | 0.06 | 13 | yes | | Bourgogne | 2.1 | 8 | yes | | Côte de Nuits | 0.3 | 11 | yes | | Volnay | 0.04 | 13 | yes | | Champagne | 1.4 | 9 | yes | | Sicilia DOC | 3.2 | 7 | tight but ok | | Mendoza | 3.0 | 7 | yes | | Mosel | 1.2 (lng) / 0.3 (lat) | 9 | too far |
Mosel is the outlier. It's a river valley — 200 km long, 5 km wide at the widest point. The bbox is dominated by the longitudinal span, but the interesting area along the river is narrow. Zoom 9 shows you the whole Eifel and Hunsrück; what you wanted was to hug the river.
The third heuristic, just for thin shapes
For regions where the bbox aspect ratio exceeds about 3:1 we drop the long axis from the max calculation and use the short one instead, then clamp:
const ratio = Math.max(lngSpan, latSpan) / Math.min(lngSpan, latSpan)
const span = ratio > 3
? Math.min(lngSpan, latSpan) * 2 // thin shape: use short axis × 2
: Math.max(lngSpan, latSpan)
This fixes Mosel (zoom 9 → 10), Rheingau, the Wachau in Austria, and Walla Walla which straddles the Oregon-Washington line in a long thin strip. It slightly over-zooms Sicilia DOC, but Sicilia is so well-known by shape that even a partial view is recognisable.
Lesson
Two pieces of information about a region are not interchangeable: how it's classified and how big it is. The bureaucratic label tells you about the wine law that governs it; the bounding box tells you what to put on screen. Designing the camera by the former gives you Pauillac at the zoom of Paso Robles. The map's job is to show the shape of the land, so let the shape decide.
#map#geo#ux