MapMath

CHAPTER 23

Interpolation and heat maps

Turning sparse sample points into continuous surfaces — IDW, kernel density, Kriging, and rendering techniques.

3 min read

You have 50 air quality sensors across a city. How do you draw a continuous pollution map? You have 1,000 crime incidents. How do you show hotspots? Interpolation and kernel density estimation answer these questions.

IDW interpolation · click to add samples

low
medium
high
peak
8 samples

Inverse Distance Weighting (IDW)

The simplest interpolation method: the estimated value at a point is a weighted average of all samples, where weight = 1/distance^p.

z^(x)=iwiziiwi,wi=1d(x,xi)p\hat{z}(x) = \frac{\sum_{i} w_i z_i}{\sum_{i} w_i}, \quad w_i = \frac{1}{d(x, x_i)^p}
function idw(queryLat, queryLon, samples, power = 2) {
  let weightedSum = 0, totalWeight = 0;

  for (const { lat, lon, value } of samples) {
    const d = haversine(queryLat, queryLon, lat, lon);
    if (d < 1) return value; // query is on a sample point
    const w = 1 / Math.pow(d, power);
    weightedSum += w * value;
    totalWeight += w;
  }

  return weightedSum / totalWeight;
}

The power parameter pp controls smoothness:

  • p=1p = 1: linear falloff, smooth but influences far points
  • p=2p = 2: common default, balanced
  • p=3p = 3: very local, sharp transitions

Kernel Density Estimation (KDE)

optional — skip if familiarrefresher

KDE and IDW solve different problems. IDW answers "what is the value here, given measured values nearby?" — useful for temperature, elevation, pollution levels. KDE answers "how concentrated are events here?" — useful for crime, accidents, shop locations. The input to KDE is just a set of points with no values; the output is a density surface.

KDE doesn't interpolate values — it estimates the density of point events. Each point spreads a smooth "bump" (the kernel) over the surrounding area.

f^(x)=1nhi=1nK ⁣(xxih)\hat{f}(x) = \frac{1}{nh} \sum_{i=1}^n K\!\left(\frac{x - x_i}{h}\right)

The Gaussian kernel is most common:

K(u)=12πeu2/2K(u) = \frac{1}{\sqrt{2\pi}} e^{-u^2/2}
function gaussianKDE(queryLat, queryLon, points, bandwidth) {
  let sum = 0;
  for (const [lat, lon] of points) {
    const d = haversine(queryLat, queryLon, lat, lon);
    const u = d / bandwidth;
    sum += Math.exp(-0.5 * u * u);
  }
  return sum / (points.length * bandwidth * Math.sqrt(2 * Math.PI));
}

The bandwidth (analogous to search radius) controls smoothness — too small and you see individual points; too large and everything blurs into one blob.

Rendering strategies

Canvas-based (CPU): Rasterise the interpolation grid into a canvas, apply a colour scale.

function renderHeatmap(canvas, samples, colorScale) {
  const ctx = canvas.getContext("2d");
  const imageData = ctx.createImageData(canvas.width, canvas.height);

  for (let px = 0; px < canvas.width; px++) {
    for (let py = 0; py < canvas.height; py++) {
      const [lat, lon] = pixelToLatLon(px, py);
      const value = idw(lat, lon, samples);
      const [r, g, b] = colorScale(value);
      const idx = (py * canvas.width + px) * 4;
      imageData.data.set([r, g, b, 200], idx);
    }
  }
  ctx.putImageData(imageData, 0, 0);
}
Chapter 23 · Paid content

Continue reading "Interpolation and heat maps"

You've reached the end of the free preview. Unlock all 22 paid chapters, including distance math, bearings, polygons, spatial indexing, and 3D map rendering — plus a downloadable PDF and the companion code repo.

  • All 22 paid chapters with worked examples
  • Downloadable PDF for offline reading
  • Companion GitHub repo (JavaScript + Python)
  • Free updates for life

Multiple payment options including Wise, PayPal, and bank transfer.