Loading
Union in Design/3D Covid-19 Demographic Map

01 Introduction

This is a modernized rebuild of a project I started years ago and never finished: a 3D choropleth map of the US, built with deck.gl, extruding every county by a COVID-19 statistic and coloring it by a census demographic. The original was abandoned mid-build — the sidebar to actually pick a demographic, a COVID stat, or a date was never wired up, and the app itself was slow enough to be barely usable. This rebuild finishes the feature and fixes the two root causes of that slowness.

  • ~3,200 US counties, extruded by COVID cases/deaths/recovered
  • Colored by census demographics — race, income, housing, 2016 vote share
  • Browse month-by-month from January 2020 through December 2022
  • MapLibre GL basemap — no API token or account required

02 Description

The original app fetched Microsoft's live Bing COVID-19 tracker CSV directly in the browser on every page load — which, when I checked it while rebuilding this, had grown to 458MB. On top of that, it joined county geometry to case data with a nested loop comparing every county against every data row, and a county-name matching bug silently broke the join for any multi-word county name. This version fixes both at the root: a one-time offline pipeline downloads and filters that data down to monthly snapshots baked directly into the app (about 8MB total, not 458MB fetched live), and the join runs once, ahead of time, using proper hash-map lookups instead of nested loops.

03 The Data Pipeline

While rewiring the join between COVID case data and county geometry, I found a bug that had been silently dropping data the whole time: the old code derived a county's name for matching by splitting on the first space, which works for "Cleburne County" but mangles anything with more than one word — "Los Angeles County" became just "Los". Every multi-word county quietly failed to join and never rendered any COVID data at all.

The other problem was scale: the live Bing COVID-19 dataset this pulls from has grown to 458MB, and the old app fetched the whole thing in the browser on every page load, then matched it against county geometry with a nested loop — comparing every county against every row. Both are fixed at the root rather than optimized in place: a one-time offline script now downloads that 458MB file exactly once, filters it down to one snapshot per month (Jan 2020 – Dec 2022), and bakes a matching key directly onto both the county geometry and the monthly data. The browser only ever fetches the small, pre-joined result — about 8MB total instead of 458MB — and switching months is a single pass over ~3,200 counties using that precomputed key, not a search through millions of rows.