diff --git a/pavement_parking/src/boundaries.rs b/pavement_parking/src/boundaries.rs
index 6b1a39c..b242a90 100644
--- a/pavement_parking/src/boundaries.rs
+++ b/pavement_parking/src/boundaries.rs
@@ -8,7 +8,7 @@ use geo::{Contains, MultiPolygon, Polygon};
use geojson::{Feature, FeatureCollection, FeatureWriter, Value};
use rstar::{primitives::GeomWithData, RTree, RTreeObject};
-use crate::{Rating, Road, Scenario};
+use crate::{Intervention, Rating, Road, Scenario};
/// Aggregate counts per LAD and CA boundaries
pub struct Boundaries {
@@ -21,6 +21,9 @@ struct Boundary {
// Given a scenario and rating, count the roads and sum the length
counts: EnumMap>,
total_length: EnumMap>,
+
+ intervention_counts: EnumMap,
+ intervention_total_length: EnumMap,
}
impl Boundaries {
@@ -47,6 +50,8 @@ impl Boundaries {
any_matches: false,
counts: EnumMap::default(),
total_length: EnumMap::default(),
+ intervention_counts: EnumMap::default(),
+ intervention_total_length: EnumMap::default(),
},
);
}
@@ -73,6 +78,8 @@ impl Boundaries {
for (scenario, lengths) in &mut info.total_length {
lengths[road.ratings[scenario]] += road.length;
}
+ info.intervention_counts[road.intervention] += 1;
+ info.intervention_total_length[road.intervention] += road.length;
}
}
}
@@ -104,6 +111,15 @@ impl Boundaries {
);
}
}
+ for (intervention, count) in &info.intervention_counts {
+ f.set_property(format!("intervention_counts_{:?}", intervention), *count);
+ }
+ for (intervention, length) in &info.intervention_total_length {
+ f.set_property(
+ format!("intervention_lengths_{:?}", intervention),
+ length.round() as usize,
+ );
+ }
summaries_writer.write_feature(&f)?;
}
diff --git a/pavement_parking/src/main.rs b/pavement_parking/src/main.rs
index 0457990..fe77811 100644
--- a/pavement_parking/src/main.rs
+++ b/pavement_parking/src/main.rs
@@ -10,7 +10,7 @@ use indicatif::{ProgressBar, ProgressStyle};
use crate::boundaries::Boundaries;
use crate::census_areas::CensusAreas;
-use crate::ratings::{Rating, Scenario};
+use crate::ratings::{Intervention, Rating, Scenario};
use crate::roads::{Class, Road};
mod boundaries;
@@ -101,7 +101,7 @@ fn tippecanoe(input: &str, output: &str) -> Result<()> {
.arg("-Z10")
.arg("-z11")
.arg("--drop-densest-as-needed");
- println!("Running: {cmd:?}");
+ println!("\nRunning: {cmd:?}");
if !cmd.status()?.success() {
bail!("tippecanoe failed");
}
diff --git a/pavement_parking/src/ratings.rs b/pavement_parking/src/ratings.rs
index 0ad6bec..05403e4 100644
--- a/pavement_parking/src/ratings.rs
+++ b/pavement_parking/src/ratings.rs
@@ -1,4 +1,4 @@
-use enum_map::Enum;
+use enum_map::{Enum, EnumMap};
use crate::Class;
@@ -64,3 +64,37 @@ impl Rating {
}
}
}
+
+/// Isomorphic to `Option`, but seems more clear to represent this way.
+#[derive(Clone, Copy, Debug, PartialEq, Enum)]
+pub enum Intervention {
+ /// No change needed (Scenario::U)
+ None,
+ Y,
+ X,
+ Z,
+ /// No scenario yields green
+ Impossible,
+}
+
+impl Intervention {
+ // TODO The ordering here may need to vary locally, especially depending on the total
+ // supply/demand of parking.
+ pub fn calculate(ratings: &EnumMap, already_one_way: bool) -> Self {
+ for (scenario, intervention) in [
+ (Scenario::U, Intervention::None),
+ (Scenario::Y, Intervention::Y),
+ (Scenario::X, Intervention::X),
+ (Scenario::Z, Intervention::Z),
+ ] {
+ if already_one_way && scenario == Scenario::X {
+ continue;
+ }
+
+ if ratings[scenario] == Rating::Green {
+ return intervention;
+ }
+ }
+ Intervention::Impossible
+ }
+}
diff --git a/pavement_parking/src/roads.rs b/pavement_parking/src/roads.rs
index 8b82c60..b241856 100644
--- a/pavement_parking/src/roads.rs
+++ b/pavement_parking/src/roads.rs
@@ -3,7 +3,7 @@ use enum_map::EnumMap;
use geo::{Coord, HaversineLength, LineString, MapCoordsInPlace};
use geojson::{Feature, Value};
-use crate::{Rating, Scenario};
+use crate::{Intervention, Rating, Scenario};
// All distance units are in meters
pub struct Road {
@@ -21,6 +21,7 @@ pub struct Road {
pub pavement_average_width: f64,
pub ratings: EnumMap,
+ pub intervention: Intervention,
}
#[derive(Clone, Copy, Debug)]
@@ -90,6 +91,7 @@ impl Road {
// TODO Only consider road width as input, or do we want to continue to also try with
// pavement width?
let ratings = EnumMap::from_fn(|scenario| Rating::new(scenario, class, road_average_width));
+ let intervention = Intervention::calculate(&ratings, direction == "one-way");
Ok(Some(Self {
geom,
@@ -104,6 +106,7 @@ impl Road {
pavement_average_width,
ratings,
+ intervention,
}))
}
@@ -122,6 +125,7 @@ impl Road {
for (scenario, rating) in self.ratings {
f.set_property(format!("rating_{:?}", scenario), rating.to_str());
}
+ f.set_property("intervention", format!("{:?}", self.intervention));
f.set_property("parkable_length", trim_meters(parkable_length));
// TODO Just debug right now, not used in the UI
diff --git a/pavement_parking/web/src/PavementLayers.svelte b/pavement_parking/web/src/PavementLayers.svelte
index 30e6f27..462457e 100644
--- a/pavement_parking/web/src/PavementLayers.svelte
+++ b/pavement_parking/web/src/PavementLayers.svelte
@@ -6,7 +6,13 @@
VectorTileSource,
hoverStateFilter,
} from "svelte-maplibre";
- import { colors, scenarios, type Mode, type Filters } from "./types";
+ import {
+ colors,
+ scenarios,
+ interventions,
+ type Mode,
+ type Filters,
+ } from "./types";
import type { ExpressionSpecification } from "maplibre-gl";
export let show: Mode;
@@ -73,6 +79,9 @@
]}
{/each}
+
+ Intervention required: {interventions[data.properties.intervention]}
+
{/if}
diff --git a/pavement_parking/web/src/SummaryLayers.svelte b/pavement_parking/web/src/SummaryLayers.svelte
index c018b0b..94438bb 100644
--- a/pavement_parking/web/src/SummaryLayers.svelte
+++ b/pavement_parking/web/src/SummaryLayers.svelte
@@ -6,7 +6,13 @@
hoverStateFilter,
FillLayer,
} from "svelte-maplibre";
- import { colors, scenarios, ratings, type Mode } from "./types";
+ import {
+ colors,
+ scenarios,
+ ratings,
+ interventions,
+ type Mode,
+ } from "./types";
export let show: Mode;
export let url: string;
@@ -30,6 +36,7 @@
{#if data?.properties}
{data.properties.name}
+
Scenario |
@@ -53,6 +60,29 @@
{/each}
+
+
+
+ Intervention |
+ Number of roads |
+ Total km |
+
+ {#each Object.entries(interventions) as [intervention, label]}
+
+ {label} |
+
+ {data.properties[
+ `intervention_counts_${intervention}`
+ ].toLocaleString()}
+ |
+
+ {(
+ data.properties[`intervention_lengths_${intervention}`] / 1000
+ ).toFixed(2)}
+ |
+
+ {/each}
+
{/if}
diff --git a/pavement_parking/web/src/types.ts b/pavement_parking/web/src/types.ts
index a6a609f..50aa0d5 100644
--- a/pavement_parking/web/src/types.ts
+++ b/pavement_parking/web/src/types.ts
@@ -52,3 +52,11 @@ export const scenarios = [
["Y", "One-way, parking both sides"],
["Z", "One-way, parking one side only"],
];
+
+export const interventions = {
+ None: "U: No change needed",
+ Y: "Y: One-way, parking both sides",
+ X: "X: Parking one side only",
+ Z: "Z: One-way, parking one side only",
+ Impossible: "Impossible: No scenario makes the road green",
+};