How Sunny Pint Works

Real-time shadow maps for UK pubs, computed client-side from LiDAR elevation data, OpenStreetMap building outlines, and geometric sun projection.

33,234
Pubs from OSM
13.4M
Building footprints
22M
Land Registry parcels
60fps
Client-side rendering

Abstract

Sunny Pint answers a simple question: which pub garden has sun right now? To do this, we combine three open datasets—OpenStreetMap building footprints, Environment Agency LiDAR elevation surveys, and HM Land Registry property boundaries—into a pre-computed dataset of building geometries with measured heights. A browser-based geometric shadow engine then projects real-time shadows from these buildings at any time of day, running entirely client-side at 60 frames per second with no backend server.

Architecture

The system consists of two parts: an offline data pipeline that processes open geospatial data into compact vector tiles, and a client-side renderer that computes and displays shadows in real time. The entire application is served as static files from a CDN—there is no application server, database, or API at runtime.

This architecture means zero ongoing server costs, global edge caching, instant page loads, and full offline support via a service worker. The computational cost of shadow projection is borne by the user's device, which modern phones handle comfortably.

OFFLINE PIPELINE OSM .pbf (1.6 GB) EA LiDAR (DSM+DTM) INSPIRE (22M plots) merge_pubs build_gpkg measure_heights match_plots generate_tiles pubs.json 33k pubs, ~1 MB gzipped tiles/*.pbf ~5,000 vector tiles Cloudflare Pages CDN static files, edge cached Browser (PWA) Shadow engine @ 60fps Canvas 2D, SunCalc, offline

Figure 1. System architecture. The offline pipeline (dashed box) processes open geospatial data into static files served via CDN. All shadow computation runs client-side.

Data Sources

SourceProvidesCoverageLicence
OpenStreetMapPub locations, building footprints, opening hoursUK-wide, community maintainedODbL
Environment Agency LiDARSurface + terrain elevation at 1m resolution~75% of EnglandOGL v3
HM Land Registry INSPIRECadastral parcel boundaries (property plots)All registered freehold in England & WalesOGL v3
Open-MeteoReal-time cloud coverGlobal, hourly updatesCC BY 4.0
SunCalcSolar position (azimuth, altitude) for any time/placeGlobalBSD-2

OpenStreetMap provides the definitive pub list via the amenity=pub tag. We chose OSM as the sole source over the Food Standards Agency (which lists food businesses, not pubs) after finding that FSA data introduced duplicate entries for restaurants operating inside pubs, incorrect geocodes, and non-pub venues such as bingo halls and barber shops.

Building Heights from LiDAR

Building heights are derived from Environment Agency airborne LiDAR surveys. Two elevation models are used: the Digital Surface Model (DSM), which captures the top of all objects including roofs, and the Digital Terrain Model (DTM), which represents the bare ground surface. Building height is the difference between the two.

DTM (ground) DSM (surface) h h 90th percentile Building A Building B h = DSM − DTM

Figure 2. Building height derivation. The DSM captures roof tops; the DTM captures bare ground. Their difference yields building height above local ground level.

For each building, we rasterise its OSM footprint onto the LiDAR grid and take the 90th percentile of height values within the footprint. This captures the ridge height of pitched roofs while ignoring chimney and antenna outliers that would inflate a simple maximum.

Height distribution (Norwich, 355,000 buildings)

0–5 m
142
5–10 m
354,453
10–15 m
577
15–20 m
120
20–25 m
28
25 m+
17

The distribution reflects Norwich's predominantly two-storey residential character. The tallest structures (up to 57 m) are church spires and multi-storey car parks. Mean height: 7.7 m, median: 8.0 m.

Computing Outdoor Areas

Each pub's outdoor area (beer garden, courtyard, terrace) is derived from HM Land Registry cadastral parcels. The pipeline finds the property parcel containing the pub, then subtracts all building footprints that intersect it—not just the pub's own building, but also sheds, garages, and neighbouring structures.

Cadastral parcel Pub Shed Garage All buildings = Outdoor area

Figure 3. Outdoor area computation. The cadastral parcel minus all intersecting building footprints yields the usable outdoor space, including interior holes.

The resulting polygon may contain interior holes where buildings are fully enclosed within the plot. These are rendered on the Canvas using the evenodd fill rule, which naturally excludes enclosed regions.

The Sunny Rating

Every pub on Sunny Pint gets a Sunny Rating from 0 to 100. It tells you what percentage of the beer garden is in direct sun on an average day. It's the headline metric of the whole site and the way to compare pubs at a glance.

ScoreLabelWhat it means
80–100Sun trapGarden in full sun for most of the day. Effectively no obstruction.
60–79Very sunnyMostly sunny across the day with some shaded patches at the edges.
40–59SunnyMeaningful sun for several hours, with real shadows from buildings.
20–39Partly shadedSome sun at peak hours, shaded most of the day.
0–19ShadedRarely gets direct sun. Probably enclosed or north-facing.

How we calculate it

Every pub's rating is computed once from real-world data, never guessed:

  1. Building shapes come from OpenStreetMap.
  2. Building heights come from Environment Agency LiDAR — laser-scanned elevation, accurate to about 10 cm vertically.
  3. The pub's outdoor seating area is the HM Land Registry cadastral parcel minus all building footprints inside it (described in the previous section).
  4. The sun's position is computed for every half-hour of daylight on the spring equinox — when day and night are equal, a fair "average day" of the year.
  5. At each half-hour, we cast geometric shadows from every nearby building (taller buildings cast longer shadows when the sun is low) and work out what fraction of the outdoor seating is actually in direct sun.
  6. The Sunny Rating is the average of those fractions, scaled to 0–100.

This is the same shadow-projection code that the live porthole renders in your browser when you select a pub — we just run it offline across a representative time grid and average the results. Single source of truth: there is no separate "rating" model that could disagree with what you see on screen.

Why fraction of garden, not square metres or hours?

We deliberately don't use total square metres of beer garden, or hours of sun per year, as the headline. Both are confounded:

Sun fraction sidesteps both problems. It normalises out garden size and day length. A 70 in Aberdeen and a 70 in Truro both mean “70% of the available sun reaches this garden” — directly comparable, latitude-fair, size-fair.

It's also robust to imperfect outdoor polygons. Cadastral parcel boundaries occasionally include extra land that isn't really garden seating — a country pub's full estate, for example. Square-metre metrics get destroyed by these outliers; sun fraction stays meaningful because it's intrinsically per-area.

What the rating doesn't include

Three things the Sunny Rating deliberately leaves out, in the interest of being honest about what the number represents:

  1. Weather. The rating is clear-sky theoretical sun. It says “this garden has the geometry to be in sun”; whether it actually is sunny on a given day depends on the clouds. The live cloud-cover badge from Open-Meteo handles that separately, and changes minute to minute.
  2. Time of year. The rating is computed against the spring equinox, when day length is the same at every UK latitude. A pub will be sunnier in summer (longer days, higher sun) and shadier in winter. The rating is the year-round “average character” of the garden — for the actual sun on a specific date and time, scrub the date picker on the live porthole.
  3. Direct sun only. “In sun” means “not in any building's shadow”. A garden that's bright from indirect or diffuse light but technically in shadow won't get credit for it. This matches the spirit of “is the seat I'd pick in the sun”.

None of these are reasons to distrust the rating — they're features. They make it defensible. Anyone can audit it by opening the porthole on the equinox at any half-hour and seeing the same shadow geometry the offline computation used.

Shadow Projection

Shadow computation uses geometric projection rather than raster ray-tracing. For each wall segment of every nearby building, the engine projects a shadow quadrilateral onto the ground plane based on the building height and the sun's current position. The roof footprint, offset by the shadow vector, completes the shadow polygon.

ground h shadow length = h / tan(α) α

Figure 4. Shadow projection geometry. The sun, building top, and shadow tip are collinear. Shadow length is h/tan(α) where α is the sun's altitude above the horizon.

shadow_length = min( h / tan(sun_altitude), 200 m )
Δlat = −shadow_length × cos(azimuth) / 111,320
Δlng = −shadow_length × sin(azimuth) / (111,320 × cos(latitude))

Shadow length is capped at 200 metres to prevent infinitely long geometry near sunrise and sunset, where the sun altitude approaches zero. At the cap, the shadow is already faint due to the daylight fraction fade.

Overlapping shadow polygons from multiple buildings are composited via an offscreen canvas: all quads are drawn opaquely to a temporary surface, which is then blitted onto the main canvas with a single alpha value. This produces uniform shadow darkness regardless of how many buildings overlap.

The Whalebone, Norwich — At 4 pm in June (altitude ~35°), a 10 m building casts a 14.3 m shadow. By 7 pm (altitude ~10°), the same building casts 56.7 m, reaching across the entire beer garden and into the street beyond.

Terrain Awareness

Elevation-adjusted building heights

A building on a hill is effectively taller from the perspective of a pub in a valley below. The shadow engine adjusts each building's height by the ground elevation difference between the building and the pub:

effective_height = building_height + (building_elevation − pub_elevation)
Pub Building h Δelev effective h pub ground level

Figure 5. Elevation-adjusted building height. A building uphill from the pub has a greater effective height (h + Δelev), casting a proportionally longer shadow.

Terrain horizon occlusion

In hilly areas, the terrain itself can block sunlight even without any building present. For each pub, the pipeline casts 36 rays (every 10° of azimuth) outward to 500 m and records the maximum terrain elevation angle in each direction. At runtime, the browser compares the sun's current altitude against this pre-computed horizon profile.

Pub × Blocked Visible

Figure 6. Terrain horizon occlusion. The dashed amber line is the horizon sightline from the pub, tangent to the hilltop. The blocked sun (red) sits below this line; its sightline intersects the hillside. The visible sun (green) sits above it, clearing the terrain.

If the sun's altitude is below the terrain horizon angle at its current azimuth, the pub is in terrain shade and no building shadows are computed. The horizon profile is stored as a compact base64-encoded byte array (48 characters per pub). Pubs in flat areas have no profile at all, as their maximum horizon angle is below 1°.

Terrain occlusion examples from hilly UK cities (Bath, Sheffield, Edinburgh) will be added once the LiDAR pipeline completes for those areas.

The Porthole

The porthole is a circular viewport rendered entirely on an HTML Canvas element. It composites eight layers per frame, from a real cartographic basemap up through shadow geometry to a decorative bezel with compass markings.

Layer stack

1Basemap3×3 grid of Stadia Alidade Smooth raster tiles at zoom 18 (~0.6 m/px)
2Night overlaySemi-transparent dark fill, opacity derived from sun altitude for smooth twilight
3ShadowsGeometric quads composited via offscreen canvas
4BuildingsFilled polygons from vector tiles; pub building highlighted
5Outdoor areaDashed green outline with evenodd holes
6Pub markerLocation pin at the pub's coordinates
7BezelMetallic ring with compass ticks and cardinal labels
8Sun/moonIcon positioned on the bezel at the sun's current bearing

The viewport covers approximately 74 metres radius at zoom 18 map resolution. Buildings up to 274 m away (viewport radius plus the 200 m shadow cap) are loaded from vector tiles, since a distant tall building can cast a shadow into the viewport even if the building itself is not visible.

Optimisations

Height-dependent building filter

A building can only affect the viewport if its shadow can physically reach it. The maximum shadow reach depends on the building's height and the minimum useful sun angle (3°, at which the daylight fraction is approximately 0.5):

reach = 74 m + min( h / tan(3°), 200 m )
HeightMax shadow at 3°ReachIncluded?
6 m (shed)114 m188 mOnly if within 188 m
8 m (house)153 m227 mOnly if within 227 m
10 m (two-storey)191 m265 mOnly if within 265 m
15 m+ (tall)200 m (cap)274 mAlways, if within 274 m

This filter eliminated 30% of buildings from the Norwich tile set without affecting shadow accuracy.

Vector tile delivery

Building geometry is served as individual z14 vector tiles (.pbf files), each covering approximately 2.4 km². A pub's 274 m loading radius spans at most four tiles, so selecting a pub triggers 1–4 small HTTP requests (typically 10–100 KB each). The service worker caches tiles for 30 days, so revisiting a neighbourhood is instant.

Compact pub data

The full building polygon for each pub (20–50 coordinate pairs) represented 70% of the pub list file. Since the renderer obtains building geometry from the vector tiles, only the polygon centroid is needed for matching. Replacing full polygons with pre-computed centroids reduced the file from 11.3 MB to 6.3 MB (approximately 1 MB after gzip).

30%
Fewer buildings (height filter)
44%
Smaller pub data file
~1 MB
Initial download (gzipped)
1–4
Tile requests per pub

Static Web Application

Sunny Pint is a Progressive Web App served entirely as static files. There is no application server, no database, and no API to maintain. The entire runtime is JavaScript executing in the browser.

This design provides several advantages:

The trade-off is that all shadow computation must run on the client. Modern smartphones handle the geometric projection for ~1,000 nearby buildings at 60fps without difficulty. The heaviest operation—the initial tile decode when selecting a pub—typically completes in under 50 ms.