{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Postcode Boundary Quality — Bank Station (1km radius)\n", "\n", "Compares postcode boundaries **before** and **after** greenspace/water subtraction." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2026-02-10T21:00:00.764910Z", "iopub.status.busy": "2026-02-10T21:00:00.764845Z", "iopub.status.idle": "2026-02-10T21:00:04.849684Z", "shell.execute_reply": "2026-02-10T21:00:04.849373Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Without greenspace: 1239 postcodes\n", "With greenspace: 1239 postcodes\n" ] } ], "source": [ "import json\n", "from pathlib import Path\n", "\n", "import folium\n", "from folium.plugins import SideBySideLayers\n", "\n", "DATA = Path(\"/tmp/bank_test\")\n", "\n", "with open(DATA / \"bank_no_greenspace.geojson\") as f:\n", " no_green = json.load(f)\n", "with open(DATA / \"bank_with_greenspace.geojson\") as f:\n", " with_green = json.load(f)\n", "\n", "print(f\"Without greenspace: {len(no_green['features'])} postcodes\")\n", "print(f\"With greenspace: {len(with_green['features'])} postcodes\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Side-by-side comparison\n", "\n", "Drag the slider left/right to compare. **Left** = original, **Right** = greenspace subtracted." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2026-02-10T21:00:04.879270Z", "iopub.status.busy": "2026-02-10T21:00:04.879058Z", "iopub.status.idle": "2026-02-10T21:00:05.027225Z", "shell.execute_reply": "2026-02-10T21:00:05.026811Z" } }, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Bank station coords\n", "BANK = [51.5133, -0.0886]\n", "\n", "m = folium.Map(location=BANK, zoom_start=15, tiles=None)\n", "folium.TileLayer(\"cartodbpositron\", name=\"Basemap\").add_to(m)\n", "\n", "# Left layer: without greenspace\n", "layer_left = folium.TileLayer(\n", " tiles=\"cartodbpositron\", name=\"Without greenspace\", overlay=True\n", ")\n", "left_fg = folium.FeatureGroup(name=\"Without greenspace\")\n", "folium.GeoJson(\n", " no_green,\n", " style_function=lambda f: {\n", " \"fillColor\": \"#3388ff\",\n", " \"color\": \"#3388ff\",\n", " \"weight\": 1,\n", " \"fillOpacity\": 0.3,\n", " },\n", " tooltip=folium.GeoJsonTooltip(fields=[\"postcode\", \"area_sqm\"]),\n", ").add_to(left_fg)\n", "left_fg.add_to(m)\n", "\n", "# Right layer: with greenspace\n", "right_fg = folium.FeatureGroup(name=\"With greenspace\")\n", "folium.GeoJson(\n", " with_green,\n", " style_function=lambda f: {\n", " \"fillColor\": \"#ff7800\",\n", " \"color\": \"#ff7800\",\n", " \"weight\": 1,\n", " \"fillOpacity\": 0.3,\n", " },\n", " tooltip=folium.GeoJsonTooltip(fields=[\"postcode\", \"area_sqm\"]),\n", ").add_to(right_fg)\n", "right_fg.add_to(m)\n", "\n", "SideBySideLayers(left_fg, right_fg).add_to(m)\n", "folium.LayerControl(collapsed=False).add_to(m)\n", "\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Overlay: greenspace-trimmed boundaries on satellite imagery" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2026-02-10T21:00:05.034794Z", "iopub.status.busy": "2026-02-10T21:00:05.034709Z", "iopub.status.idle": "2026-02-10T21:00:05.087918Z", "shell.execute_reply": "2026-02-10T21:00:05.087654Z" } }, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2 = folium.Map(location=BANK, zoom_start=16, tiles=\"cartodbpositron\")\n", "\n", "# Satellite tile layer\n", "folium.TileLayer(\n", " tiles=\"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}\",\n", " attr=\"Esri\",\n", " name=\"Satellite\",\n", ").add_to(m2)\n", "\n", "# Postcode boundaries with greenspace removed\n", "folium.GeoJson(\n", " with_green,\n", " name=\"Postcodes (greenspace removed)\",\n", " style_function=lambda f: {\n", " \"fillColor\": \"#00d4aa\",\n", " \"color\": \"white\",\n", " \"weight\": 1.5,\n", " \"fillOpacity\": 0.35,\n", " },\n", " tooltip=folium.GeoJsonTooltip(\n", " fields=[\"postcode\", \"area_sqm\"],\n", " aliases=[\"Postcode\", \"Area (sqm)\"],\n", " ),\n", ").add_to(m2)\n", "\n", "# Bank station marker\n", "folium.Marker(BANK, popup=\"Bank Station\", icon=folium.Icon(color=\"red\")).add_to(m2)\n", "\n", "folium.LayerControl(collapsed=False).add_to(m2)\n", "m2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Highlight postcodes most affected by greenspace subtraction" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2026-02-10T21:00:05.090898Z", "iopub.status.busy": "2026-02-10T21:00:05.090833Z", "iopub.status.idle": "2026-02-10T21:00:05.093987Z", "shell.execute_reply": "2026-02-10T21:00:05.093756Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Postcode Before After Removed\n", "----------------------------------------\n", "EC2Y 8DP 3092 313 89.9%\n", "EC2Y 8DN 1886 235 87.5%\n", "EC4R 3TB 221 36 83.7%\n", "SE1 2JH 1757 417 76.3%\n", "EC2Y 8ND 3416 1079 68.4%\n", "EC4V 3PA 3320 1330 59.9%\n", "EC2Y 8BD 3567 1547 56.6%\n", "EC4V 3PT 2 1 50.0%\n", "EC3R 6AP 79 45 43.0%\n", "EC3R 6DX 8746 5239 40.1%\n", "EC4V 5ER 2682 1613 39.9%\n", "EC2Y 8BB 8918 5459 38.8%\n", "EC2Y 8BT 630 413 34.4%\n", "EC4M 8AP 5087 3496 31.3%\n", "EC2Y 8BE 9339 7143 23.5%\n", "SE1 9DE 5091 4538 10.9%\n", "EC4M 9AD 2492 2271 8.9%\n", "EC4M 8AD 12942 12021 7.1%\n", "EC4M 9AH 17851 16718 6.3%\n", "EC2Y 8BX 8080 7611 5.8%\n" ] } ], "source": [ "# Build area lookup from both sets\n", "areas_before = {\n", " f[\"properties\"][\"postcode\"]: f[\"properties\"][\"area_sqm\"]\n", " for f in no_green[\"features\"]\n", "}\n", "areas_after = {\n", " f[\"properties\"][\"postcode\"]: f[\"properties\"][\"area_sqm\"]\n", " for f in with_green[\"features\"]\n", "}\n", "\n", "# Compute percentage removed\n", "diffs = []\n", "for pc, before in areas_before.items():\n", " after = areas_after.get(pc, 0)\n", " if before > 0:\n", " pct = 100 * (before - after) / before\n", " if pct > 1:\n", " diffs.append((pc, before, after, pct))\n", "diffs.sort(key=lambda x: -x[3])\n", "\n", "print(f\"{'Postcode':<12} {'Before':>8} {'After':>8} {'Removed':>8}\")\n", "print(\"-\" * 40)\n", "for pc, before, after, pct in diffs[:20]:\n", " print(f\"{pc:<12} {before:>7} {after:>7} {pct:>5.1f}%\")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2026-02-10T21:00:05.094670Z", "iopub.status.busy": "2026-02-10T21:00:05.094606Z", "iopub.status.idle": "2026-02-10T21:00:05.149304Z", "shell.execute_reply": "2026-02-10T21:00:05.149003Z" } }, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import branca.colormap as cm\n", "\n", "# Map showing % area removed as a heatmap\n", "m3 = folium.Map(location=BANK, zoom_start=15, tiles=\"cartodbpositron\")\n", "\n", "# Build a feature collection with removal percentage\n", "diff_lookup = {pc: pct for pc, _, _, pct in diffs}\n", "max_pct = max((pct for _, _, _, pct in diffs), default=1)\n", "\n", "colormap = cm.LinearColormap(\n", " colors=[\"#ffffcc\", \"#fd8d3c\", \"#e31a1c\", \"#800026\"],\n", " vmin=0,\n", " vmax=min(max_pct, 90),\n", " caption=\"% area removed by greenspace\",\n", ")\n", "\n", "\n", "# Show original boundaries, colored by how much was removed\n", "def style_by_removal(feature):\n", " pc = feature[\"properties\"][\"postcode\"]\n", " pct = diff_lookup.get(pc, 0)\n", " if pct <= 1:\n", " return {\n", " \"fillColor\": \"#cccccc\",\n", " \"color\": \"#999\",\n", " \"weight\": 0.5,\n", " \"fillOpacity\": 0.15,\n", " }\n", " return {\n", " \"fillColor\": colormap(min(pct, 90)),\n", " \"color\": \"white\",\n", " \"weight\": 1,\n", " \"fillOpacity\": 0.6,\n", " }\n", "\n", "\n", "folium.GeoJson(\n", " no_green,\n", " name=\"Greenspace removal %\",\n", " style_function=style_by_removal,\n", " tooltip=folium.GeoJsonTooltip(\n", " fields=[\"postcode\", \"area_sqm\"],\n", " aliases=[\"Postcode\", \"Area (sqm)\"],\n", " ),\n", ").add_to(m3)\n", "\n", "colormap.add_to(m3)\n", "m3" ] } ], "metadata": { "kernelspec": { "display_name": "property-map", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.12" } }, "nbformat": 4, "nbformat_minor": 4 }