Skip to content

Instantly share code, notes, and snippets.

@hyperreality
Last active October 30, 2025 02:24
Show Gist options
  • Select an option

  • Save hyperreality/adf9f652919b7b2410f0c924e37dd03a to your computer and use it in GitHub Desktop.

Select an option

Save hyperreality/adf9f652919b7b2410f0c924e37dd03a to your computer and use it in GitHub Desktop.
Overpass Get England District Cyclable Density CSV
import requests
import csv
import time
with open("district_merged2.csv", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
districts = [row for row in reader if row]
def length_query(selector):
return f"""
[out:json][timeout:180];
{selector};
map_to_area->.district;
// Collect all rideable categories
(
/* Bridleways */
way(area.district)
["highway"="bridleway"]
["access"!~"no"]
["bicycle"!~"no"];
/* Bridleways by designation on other highway types */
way(area.district)
["designation"~"public_bridleway"]
["access"!~"no"]
["bicycle"!~"no"];
/* ── Restricted byways ── */
way(area.district)
["designation"="restricted_byway"]
["access"!~"no"]
["bicycle"!~"no"];
/* ── BOATs ── */
way(area.district)
["designation"="byway_open_to_all_traffic"]
["access"!~"no"]
["bicycle"!~"no"];
/* ── Cycleways ── */
way(area.district)
["highway"="cycleway"]
["access"!~"no"]
["bicycle"!~"no"];
way(area.district)
["cycleway"="yes"]
["access"!~"no"]
["bicycle"!~"no"];
/* ── Shared-use paths & tracks ── */
way(area.district)
["highway"="footway"]
["area"!="yes"]
["bicycle"~"^(designated|yes|permissive)$"];
way(area.district)
["highway"="path"]
["area"!="yes"]
["bicycle"~"^(designated|yes|permissive)$"];
way(area.district)
["highway"="track"]
["area"!="yes"]
["bicycle"~"^(designated|yes|permissive)$"];
)->.all;
// Aggregate to a single record with total length
for.all (1) {{
make stats
ways=count(ways),
length_km=sum(length())/1000;
out;
}}
// Display
out geom;
"""
print("District,Area,CyclableLength,CyclableDensity")
def overpass_call(query):
while True:
url = "https://overpass-api.de/api/interpreter"
response = requests.get(url, params={"data": query})
# print(response.text)
if "probably too busy" in response.text or "rate_limited" in response.text:
# print(response.text)
# print("sleeping")
time.sleep(10)
else:
return response.json()
seen = False
for district in districts:
# jank
if "," in district["Name"]:
district["Name"] = '"' + district["Name"] + '"'
print(district["Name"], end=",")
print(district["Area"], end=",")
selector = f"""rel
["boundary"="administrative"]
["type"="boundary"]
["ref:gss"="{district["GSS"]}"]"""
if district["Name"] in ["East Staffordshire", "Lichfield", "Stafford", "Staffordshire Moorlands", "Tamworth", "North Warwickshire", "Nuneaton and Bedworth", "Rugby", "Warwick"]:
selector = f"""rel["boundary"="administrative"]["admin_level"="8"]["name"="{district["Name"]}"]"""
query = length_query(selector)
raw = overpass_call(query)
elements = raw["elements"]
if len(elements) == 0:
print("No response")
exit()
first_element = raw["elements"][0]
length = first_element["tags"]["length_km"]
print(round(float(length), 2), end=",")
print(round(1000 * float(length) / int(district["Area"])), end="")
print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment