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