Skip to content

Commit

Permalink
Calculate the intervention needed to rate a road green. Display it in
Browse files Browse the repository at this point in the history
simple ways.
  • Loading branch information
dabreegster committed Aug 24, 2024
1 parent 873239a commit 2762efa
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 7 deletions.
18 changes: 17 additions & 1 deletion pavement_parking/src/boundaries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -21,6 +21,9 @@ struct Boundary {
// Given a scenario and rating, count the roads and sum the length
counts: EnumMap<Scenario, EnumMap<Rating, usize>>,
total_length: EnumMap<Scenario, EnumMap<Rating, f64>>,

intervention_counts: EnumMap<Intervention, usize>,
intervention_total_length: EnumMap<Intervention, f64>,
}

impl Boundaries {
Expand All @@ -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(),
},
);
}
Expand All @@ -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;
}
}
}
Expand Down Expand Up @@ -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)?;
}
Expand Down
4 changes: 2 additions & 2 deletions pavement_parking/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
}
Expand Down
36 changes: 35 additions & 1 deletion pavement_parking/src/ratings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use enum_map::Enum;
use enum_map::{Enum, EnumMap};

use crate::Class;

Expand Down Expand Up @@ -64,3 +64,37 @@ impl Rating {
}
}
}

/// Isomorphic to `Option<Scenario>`, 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<Scenario, Rating>, 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
}
}
6 changes: 5 additions & 1 deletion pavement_parking/src/roads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -21,6 +21,7 @@ pub struct Road {
pub pavement_average_width: f64,

pub ratings: EnumMap<Scenario, Rating>,
pub intervention: Intervention,
}

#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -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,
Expand All @@ -104,6 +106,7 @@ impl Road {
pavement_average_width,

ratings,
intervention,
}))
}

Expand All @@ -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
Expand Down
11 changes: 10 additions & 1 deletion pavement_parking/web/src/PavementLayers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,6 +79,9 @@
]}
</p>
{/each}
<p>
Intervention required: {interventions[data.properties.intervention]}
</p>
{/if}
</Popup>
</LineLayer>
Expand Down
32 changes: 31 additions & 1 deletion pavement_parking/web/src/SummaryLayers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +36,7 @@
<Popup openOn="hover" let:data>
{#if data?.properties}
<h1>{data.properties.name}</h1>

<table>
<tr>
<th>Scenario</th>
Expand All @@ -53,6 +60,29 @@
</tr>
{/each}
</table>

<table>
<tr>
<th>Intervention</th>
<th>Number of roads</th>
<th>Total km</th>
</tr>
{#each Object.entries(interventions) as [intervention, label]}
<tr>
<th>{label}</th>
<td>
{data.properties[
`intervention_counts_${intervention}`
].toLocaleString()}
</td>
<td>
{(
data.properties[`intervention_lengths_${intervention}`] / 1000
).toFixed(2)}
</td>
</tr>
{/each}
</table>
{/if}
</Popup>
</FillLayer>
Expand Down
8 changes: 8 additions & 0 deletions pavement_parking/web/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
};

0 comments on commit 2762efa

Please sign in to comment.