Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion examples/edinburgh/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"pattern": {
"BetweenZones": {
"zones_path": "zones.geojson",
"csv_path": "od.csv"
"csv_path": "od.csv",
"origin_zone_centroid_fallback": false,
"destination_zone_centroid_fallback": false
}
},
"origins_path": "buildings.geojson",
Expand Down
4 changes: 3 additions & 1 deletion examples/england_2011_home_to_work/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"pattern": {
"BetweenZones": {
"zones_path": "zones.geojson",
"csv_path": "od.csv"
"csv_path": "od.csv",
"origin_zone_centroid_fallback": false,
"destination_zone_centroid_fallback": false
}
},
"origins_path": "buildings.geojson",
Expand Down
11 changes: 11 additions & 0 deletions examples/england_2021_home_to_work/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 2021 England home-to-work

- Origins: all buildings
- Destinations: all buildings (for now)
- Flows include all modes (including work-from-home)

Data sources:

- [ODWP01EW](https://www.nomisweb.co.uk/sources/census_2021_od): UK OA to OA, about 9 million rows
- There are [major caveats](https://www.ons.gov.uk/peoplepopulationandcommunity/populationandmigration/populationestimates/bulletins/origindestinationdataenglandandwales/census2021#origin-destination-workplace-data) with OD data from 2021 due to COVID.
- OA zone geojson from ONS Geography
18 changes: 18 additions & 0 deletions examples/england_2021_home_to_work/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"requests": {
"description": "2021 home to work trips in England, for any mode of commuting. From OA-level data from ODWP01EW. Origins and destinations are all buildings.",
"pattern": {
"BetweenZones": {
"zones_path": "zones.geojson",
"csv_path": "od.csv",
"origin_zone_centroid_fallback": true,
"destination_zone_centroid_fallback": true
}
},
"origins_path": "buildings.geojson",
"destinations_path": "buildings.geojson"
},
"cost": "Distance",
"uptake": "Identity",
"lts": "BikeOttawa"
}
82 changes: 82 additions & 0 deletions examples/england_2021_home_to_work/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import csv
import json
import os.path
import sys

from utils import *


def makeOSM():
download(
url="https://download.geofabrik.de/europe/great-britain/england-latest.osm.pbf",
outputFilename="input/input.osm.pbf",
)


def makeOrigins():
extractCentroids(
osmInput="input/input.osm.pbf", geojsonOutput="input/buildings.geojson"
)


def makeDestinations():
# Same as origins
pass


def makeZones():
download(
url="https://github.com/dabreegster/uk-boundaries/raw/main/2021_output_areas.geojson.gz",
outputFilename="input/zones.geojson.gz",
)
run(["gunzip", "input/zones.geojson.gz"])

with open("input/zones.geojson") as f1:
gj = json.load(f1)
gj["features"] = list(
filter(lambda f: f["properties"]["OA21CD"][0] == "E", gj["features"])
)
for f in gj["features"]:
props = {"name": f["properties"]["OA21CD"]}
f["properties"] = props

with open("input/zones.geojson", "w") as f2:
f2.write(json.dumps(gj))


def makeOD():
download(
url="https://www.nomisweb.co.uk/output/census/2021/odwp01ew.zip",
outputFilename="input/odwp01ew.zip",
)
run(["unzip", "input/odwp01ew.zip", "-d", "input"])
with open("input/ODWP01EW_OA.csv") as f1:
with open("input/od.csv", "w") as f2:
writer = csv.DictWriter(f2, fieldnames=["from", "to", "count"])
writer.writeheader()

for row in csv.DictReader(f1):
zone1 = row["Output Areas code"]
zone2 = row["OA of workplace code"]
if zone1[0] == "E" and zone2[0] == "E":
# Ideally there'd be a split by commute mode. This dataset
# includes work-from-home, but just include all trips.
count = int(row["Count"])
if count > 0:
writer.writerow(
{
"from": zone1,
"to": zone2,
"count": count,
}
)


if __name__ == "__main__":
checkDependencies()
run(["mkdir", "-p", "input"])
makeOSM()
makeOrigins()
makeDestinations()
makeZones()
makeOD()
1 change: 1 addition & 0 deletions examples/england_2021_home_to_work/utils.py
4 changes: 3 additions & 1 deletion examples/lisbon/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"pattern": {
"BetweenZones": {
"zones_path": "zones.geojson",
"csv_path": "od.csv"
"csv_path": "od.csv",
"origin_zone_centroid_fallback": false,
"destination_zone_centroid_fallback": false
}
},
"origins_path": "buildings.geojson",
Expand Down
1 change: 1 addition & 0 deletions examples/run_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ run_example lisbon

# Huge
run_example england_2011_home_to_work
run_example england_2021_home_to_work
run_example seattle

python3 summarize_results.py */output/metadata.json
4 changes: 4 additions & 0 deletions od2net/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ pub enum ODPattern {
/// Path to a CSV file that must have 3 columns "from", "to", and "count". The first
/// two must match zone names. "count" must be an integer.
csv_path: String,
/// If an origin zone doesn't have any matching origin points, use the zone's centroid instead.
origin_zone_centroid_fallback: bool,
/// If a destination zone doesn't have any matching origin points, use the zone's centroid instead.
destination_zone_centroid_fallback: bool,
},
ZoneToPoint {
/// Path to a GeoJSON file containing Polygons and MultiPolygons with a "name" property
Expand Down
13 changes: 10 additions & 3 deletions od2net/src/od.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub fn generate_requests(
ODPattern::BetweenZones {
zones_path,
csv_path,
origin_zone_centroid_fallback,
destination_zone_centroid_fallback,
} => {
let zones_path = format!("{input_directory}/{zones_path}");
let csv_path = format!("{input_directory}/{csv_path}");
Expand All @@ -91,9 +93,14 @@ pub fn generate_requests(
let zones = load_zones(&zones_path)?;
timer.stop();
timer.start("Matching points to zones");
let origins_per_zone = points_per_polygon("origin", origins, &zones, false)?;
let destinations_per_zone =
points_per_polygon("destination", destinations, &zones, false)?;
let origins_per_zone =
points_per_polygon("origin", origins, &zones, *origin_zone_centroid_fallback)?;
let destinations_per_zone = points_per_polygon(
"destination",
destinations,
&zones,
*destination_zone_centroid_fallback,
)?;
timer.stop();

timer.start(format!("Generating requests from {csv_path}"));
Expand Down