Mechanically switch to British spelling of neighbourhood.

This is yet another breaking change in the LTN proposal format
This commit is contained in:
Dustin Carlino 2022-06-12 09:10:27 +01:00
parent f128e3d67e
commit 4f0b150d68
23 changed files with 415 additions and 397 deletions

View File

@ -11,23 +11,23 @@ use widgetry::{
};
use crate::filters::auto::Heuristic;
use crate::{colors, App, Neighborhood, NeighborhoodID, Transition};
use crate::{colors, App, Neighbourhood, NeighbourhoodID, Transition};
pub struct BrowseNeighborhoods {
pub struct BrowseNeighbourhoods {
top_panel: Panel,
left_panel: Panel,
world: World<NeighborhoodID>,
world: World<NeighbourhoodID>,
draw_over_roads: ToggleZoomed,
labels: DrawRoadLabels,
draw_boundary_roads: ToggleZoomed,
}
impl BrowseNeighborhoods {
impl BrowseNeighbourhoods {
pub fn new_state(ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
map_gui::tools::update_url_map_name(app);
let (world, draw_over_roads) =
ctx.loading_screen("calculate neighborhoods", |ctx, timer| {
ctx.loading_screen("calculate neighbourhoods", |ctx, timer| {
if &app.session.partitioning.map != app.map.get_name() {
app.session.alt_proposals = crate::save::AltProposals::new();
crate::clear_current_proposal(ctx, app, timer);
@ -50,7 +50,7 @@ impl BrowseNeighborhoods {
]),
)
.build(ctx);
Box::new(BrowseNeighborhoods {
Box::new(BrowseNeighbourhoods {
top_panel,
left_panel,
world,
@ -62,14 +62,14 @@ impl BrowseNeighborhoods {
pub fn button(ctx: &EventCtx, app: &App) -> Widget {
ctx.style()
.btn_back("Browse neighborhoods")
.btn_back("Browse neighbourhoods")
.hotkey(Key::Escape)
.build_def(ctx)
.hide(app.session.consultation.is_some())
}
}
impl State<App> for BrowseNeighborhoods {
impl State<App> for BrowseNeighbourhoods {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
if let Some(t) = crate::components::TopPanel::event(ctx, app, &mut self.top_panel, help) {
return t;
@ -85,32 +85,32 @@ impl State<App> for BrowseNeighborhoods {
));
}
"Automatically place filters" => {
ctx.loading_screen("automatically filter all neighborhoods", |ctx, timer| {
ctx.loading_screen("automatically filter all neighbourhoods", |ctx, timer| {
timer.start_iter(
"filter neighborhood",
app.session.partitioning.all_neighborhoods().len(),
"filter neighbourhood",
app.session.partitioning.all_neighbourhoods().len(),
);
for id in app
.session
.partitioning
.all_neighborhoods()
.all_neighbourhoods()
.keys()
.cloned()
.collect::<Vec<_>>()
{
timer.next();
let neighborhood = Neighborhood::new(ctx, app, id);
let neighbourhood = Neighbourhood::new(ctx, app, id);
// Ignore errors
let _ = app.session.heuristic.apply(ctx, app, &neighborhood, timer);
let _ = app.session.heuristic.apply(ctx, app, &neighbourhood, timer);
}
});
return Transition::Replace(BrowseNeighborhoods::new_state(ctx, app));
return Transition::Replace(BrowseNeighbourhoods::new_state(ctx, app));
}
x => {
return crate::save::AltProposals::handle_action(
ctx,
app,
crate::save::PreserveState::BrowseNeighborhoods,
crate::save::PreserveState::BrowseNeighbourhoods,
x,
)
.unwrap();
@ -119,7 +119,7 @@ impl State<App> for BrowseNeighborhoods {
Outcome::Changed(x) => {
if x == "Advanced features" {
app.opts.dev = self.left_panel.is_checked("Advanced features");
return Transition::Replace(BrowseNeighborhoods::new_state(ctx, app));
return Transition::Replace(BrowseNeighbourhoods::new_state(ctx, app));
}
if x == "heuristic" {
app.session.heuristic = self.left_panel.dropdown_value("heuristic");
@ -128,7 +128,7 @@ impl State<App> for BrowseNeighborhoods {
app.session.highlight_boundary_roads =
self.left_panel.is_checked("highlight boundary roads");
} else {
app.session.draw_neighborhood_style =
app.session.draw_neighbourhood_style =
self.left_panel.dropdown_value("style");
}
@ -168,11 +168,11 @@ impl State<App> for BrowseNeighborhoods {
}
}
fn make_world(ctx: &mut EventCtx, app: &App, timer: &mut Timer) -> World<NeighborhoodID> {
fn make_world(ctx: &mut EventCtx, app: &App, timer: &mut Timer) -> World<NeighbourhoodID> {
let mut world = World::bounded(app.map.get_bounds());
let map = &app.map;
for (id, info) in app.session.partitioning.all_neighborhoods() {
match app.session.draw_neighborhood_style {
for (id, info) in app.session.partitioning.all_neighbourhoods() {
match app.session.draw_neighbourhood_style {
Style::SimpleColoring => {
world
.add(*id)
@ -183,10 +183,10 @@ fn make_world(ctx: &mut EventCtx, app: &App, timer: &mut Timer) -> World<Neighbo
.build(ctx);
}
Style::Cells => {
// TODO The cell colors are confusing alongside the other neighborhood colors. I
// TODO The cell colors are confusing alongside the other neighbourhood colors. I
// tried greying out everything else, but then the view is too jumpy.
let neighborhood = Neighborhood::new(ctx, app, *id);
let render_cells = crate::draw_cells::RenderCells::new(map, &neighborhood);
let neighbourhood = Neighbourhood::new(ctx, app, *id);
let render_cells = crate::draw_cells::RenderCells::new(map, &neighbourhood);
let hovered_batch = render_cells.draw();
world
.add(*id)
@ -197,10 +197,10 @@ fn make_world(ctx: &mut EventCtx, app: &App, timer: &mut Timer) -> World<Neighbo
.build(ctx);
}
Style::Quietness => {
let neighborhood = Neighborhood::new(ctx, app, *id);
let shortcuts = crate::shortcuts::find_shortcuts(app, &neighborhood, timer);
let neighbourhood = Neighbourhood::new(ctx, app, *id);
let shortcuts = crate::shortcuts::find_shortcuts(app, &neighbourhood, timer);
let (quiet_streets, total_streets) =
shortcuts.quiet_and_total_streets(&neighborhood);
shortcuts.quiet_and_total_streets(&neighbourhood);
let pct = if total_streets == 0 {
0.0
} else {
@ -231,22 +231,22 @@ fn make_world(ctx: &mut EventCtx, app: &App, timer: &mut Timer) -> World<Neighbo
}
fn draw_over_roads(ctx: &mut EventCtx, app: &App, timer: &mut Timer) -> ToggleZoomed {
if app.session.draw_neighborhood_style != Style::Shortcuts {
if app.session.draw_neighbourhood_style != Style::Shortcuts {
return ToggleZoomed::empty(ctx);
}
let mut count_per_road = Counter::new();
let mut count_per_intersection = Counter::new();
for id in app.session.partitioning.all_neighborhoods().keys() {
let neighborhood = Neighborhood::new(ctx, app, *id);
let shortcuts = crate::shortcuts::find_shortcuts(app, &neighborhood, timer);
for id in app.session.partitioning.all_neighbourhoods().keys() {
let neighbourhood = Neighbourhood::new(ctx, app, *id);
let shortcuts = crate::shortcuts::find_shortcuts(app, &neighbourhood, timer);
count_per_road.extend(shortcuts.count_per_road);
count_per_intersection.extend(shortcuts.count_per_intersection);
}
// TODO It's a bit weird to draw one heatmap covering streets in every neighborhood. The
// shortcuts are calculated per neighborhood, but now we're showing them all together, as if
// TODO It's a bit weird to draw one heatmap covering streets in every neighbourhood. The
// shortcuts are calculated per neighbourhood, but now we're showing them all together, as if
// it's the impact prediction mode using a demand model.
let mut colorer = ColorNetwork::no_fading(app);
colorer.ranked_roads(count_per_road, &app.cs.good_to_bad_red);
@ -258,7 +258,7 @@ pub fn draw_boundary_roads(ctx: &EventCtx, app: &App) -> ToggleZoomed {
let mut seen_roads = HashSet::new();
let mut seen_borders = HashSet::new();
let mut batch = ToggleZoomed::builder();
for info in app.session.partitioning.all_neighborhoods().values() {
for info in app.session.partitioning.all_neighbourhoods().values() {
for id in &info.block.perimeter.roads {
let r = id.road;
if seen_roads.contains(&r) {
@ -345,7 +345,7 @@ fn help() -> Vec<&'static str> {
vec![
"Basic map navigation: click and drag to pan, swipe or scroll to zoom",
"",
"Click a neighborhood to analyze it. You can adjust boundaries there.",
"Click a neighbourhood to analyze it. You can adjust boundaries there.",
]
}
@ -357,11 +357,11 @@ fn advanced_panel(ctx: &EventCtx, app: &App) -> Widget {
Line("Advanced features").small_heading().into_widget(ctx),
Widget::col(vec![
Widget::row(vec![
"Draw neighborhoods:".text_widget(ctx).centered_vert(),
"Draw neighbourhoods:".text_widget(ctx).centered_vert(),
Widget::dropdown(
ctx,
"style",
app.session.draw_neighborhood_style,
app.session.draw_neighbourhood_style,
vec![
Choice::new("simple", Style::SimpleColoring),
Choice::new("cells", Style::Cells),

View File

@ -1,9 +1,9 @@
use widgetry::Color;
lazy_static::lazy_static! {
/// Rotate through these colors for neighborhoods. Use 4-color (ehem, 6-color?) theorem to make
/// Rotate through these colors for neighbourhoods. Use 4-color (ehem, 6-color?) theorem to make
/// adjacent things different
pub static ref NEIGHBORHOODS: [Color; 6] = [
pub static ref NEIGHBOURHOODS: [Color; 6] = [
Color::BLUE.alpha(0.3),
Color::YELLOW.alpha(0.3),
Color::GREEN.alpha(0.3),

View File

@ -5,7 +5,7 @@ use map_model::{IntersectionID, PathConstraints, Perimeter};
use widgetry::tools::PolyLineLasso;
use widgetry::{DrawBaselayer, EventCtx, GfxCtx, Key, Line, ScreenPt, State, Text, Widget};
use crate::{after_edit, App, DiagonalFilter, Neighborhood, Transition};
use crate::{after_edit, App, DiagonalFilter, Neighbourhood, Transition};
pub struct FreehandFilters {
lasso: PolyLineLasso,
@ -18,13 +18,13 @@ pub struct FreehandFilters {
impl FreehandFilters {
pub fn new_state(
ctx: &EventCtx,
neighborhood: &Neighborhood,
neighbourhood: &Neighbourhood,
instructions_at: ScreenPt,
) -> Box<dyn State<App>> {
Box::new(Self {
lasso: PolyLineLasso::new(),
perimeter: neighborhood.orig_perimeter.clone(),
interior_intersections: neighborhood.interior_intersections.clone(),
perimeter: neighbourhood.orig_perimeter.clone(),
interior_intersections: neighbourhood.interior_intersections.clone(),
instructions_at,
instructions: Text::from_all(vec![
Line("Click and drag").fg(ctx.style().text_hotkey_color),

View File

@ -5,7 +5,7 @@ use widgetry::{
VerticalAlignment, Widget,
};
use crate::{App, BrowseNeighborhoods, Transition};
use crate::{App, BrowseNeighbourhoods, Transition};
pub struct TopPanel;
@ -19,7 +19,7 @@ impl TopPanel {
Line(if consultation {
"East Bristol Liveable Neighbourhood"
} else {
"Low traffic neighborhoods"
"Low traffic neighbourhoods"
})
.small_heading()
.into_widget(ctx)
@ -81,7 +81,7 @@ impl TopPanel {
ctx,
app,
map_gui::tools::Executable::LTN,
Box::new(|ctx, app, _| BrowseNeighborhoods::new_state(ctx, app)),
Box::new(|ctx, app, _| BrowseNeighbourhoods::new_state(ctx, app)),
),
]))
} else {
@ -92,7 +92,7 @@ impl TopPanel {
ctx,
app,
Box::new(|ctx, app| {
Transition::Replace(BrowseNeighborhoods::new_state(ctx, app))
Transition::Replace(BrowseNeighbourhoods::new_state(ctx, app))
}),
))),
"search" => Some(Transition::Push(map_gui::tools::Navigator::new_state(

View File

@ -7,29 +7,29 @@ use widgetry::{
DrawBaselayer, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, TextExt, Toggle, Widget,
};
use crate::edit::{EditNeighborhood, Tab};
use crate::edit::{EditNeighbourhood, Tab};
use crate::filters::auto::Heuristic;
use crate::shortcuts::find_shortcuts;
use crate::{colors, App, Neighborhood, NeighborhoodID, Transition};
use crate::{colors, App, Neighbourhood, NeighbourhoodID, Transition};
pub struct Viewer {
top_panel: Panel,
left_panel: Panel,
neighborhood: Neighborhood,
neighbourhood: Neighbourhood,
draw_top_layer: ToggleZoomed,
edit: EditNeighborhood,
edit: EditNeighbourhood,
}
impl Viewer {
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighborhoodID) -> Box<dyn State<App>> {
let neighborhood = Neighborhood::new(ctx, app, id);
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighbourhoodID) -> Box<dyn State<App>> {
let neighbourhood = Neighbourhood::new(ctx, app, id);
let mut viewer = Viewer {
top_panel: crate::components::TopPanel::panel(ctx, app),
left_panel: Panel::empty(ctx),
neighborhood,
neighbourhood,
draw_top_layer: ToggleZoomed::empty(ctx),
edit: EditNeighborhood::temporary(),
edit: EditNeighbourhood::temporary(),
};
viewer.update(ctx, app);
Box::new(viewer)
@ -37,7 +37,7 @@ impl Viewer {
fn update(&mut self, ctx: &mut EventCtx, app: &App) {
let disconnected_cells = self
.neighborhood
.neighbourhood
.cells
.iter()
.filter(|c| c.is_disconnected())
@ -57,10 +57,10 @@ impl Viewer {
&self.top_panel,
Widget::col(vec![
format!(
"Neighborhood area: {}",
"Neighbourhood area: {}",
app.session
.partitioning
.neighborhood_area_km2(self.neighborhood.id)
.neighbourhood_area_km2(self.neighbourhood.id)
)
.text_widget(ctx),
warning.text_widget(ctx),
@ -69,7 +69,7 @@ impl Viewer {
)
.build(ctx);
let (edit, draw_top_layer) = setup_editing(ctx, app, &self.neighborhood);
let (edit, draw_top_layer) = setup_editing(ctx, app, &self.neighbourhood);
self.edit = edit;
self.draw_top_layer = draw_top_layer;
}
@ -83,13 +83,17 @@ impl State<App> for Viewer {
match self.left_panel.event(ctx) {
Outcome::Clicked(x) => {
if x == "Automatically place filters" {
match ctx.loading_screen("automatically filter a neighborhood", |ctx, timer| {
app.session
.heuristic
.apply(ctx, app, &self.neighborhood, timer)
}) {
match ctx.loading_screen(
"automatically filter a neighbourhood",
|ctx, timer| {
app.session
.heuristic
.apply(ctx, app, &self.neighbourhood, timer)
},
) {
Ok(()) => {
self.neighborhood = Neighborhood::new(ctx, app, self.neighborhood.id);
self.neighbourhood =
Neighbourhood::new(ctx, app, self.neighbourhood.id);
self.update(ctx, app);
return Transition::Keep;
}
@ -106,14 +110,14 @@ impl State<App> for Viewer {
crate::customize_boundary::CustomizeBoundary::new_state(
ctx,
app,
self.neighborhood.id,
self.neighbourhood.id,
),
);
} else if let Some(t) = self.edit.handle_panel_action(
ctx,
app,
x.as_ref(),
&self.neighborhood,
&self.neighbourhood,
&self.left_panel,
) {
return t;
@ -125,7 +129,7 @@ impl State<App> for Viewer {
crate::save::PreserveState::Connectivity(
app.session
.partitioning
.all_blocks_in_neighborhood(self.neighborhood.id),
.all_blocks_in_neighbourhood(self.neighbourhood.id),
),
&x,
)
@ -142,7 +146,7 @@ impl State<App> for Viewer {
app.session.heuristic = self.left_panel.dropdown_value("heuristic");
if x != "heuristic" {
let (edit, draw_top_layer) = setup_editing(ctx, app, &self.neighborhood);
let (edit, draw_top_layer) = setup_editing(ctx, app, &self.neighbourhood);
self.edit = edit;
self.draw_top_layer = draw_top_layer;
}
@ -151,7 +155,7 @@ impl State<App> for Viewer {
}
if self.edit.event(ctx, app) {
self.neighborhood = Neighborhood::new(ctx, app, self.neighborhood.id);
self.neighbourhood = Neighbourhood::new(ctx, app, self.neighbourhood.id);
self.update(ctx, app);
}
@ -164,7 +168,7 @@ impl State<App> for Viewer {
fn draw(&self, g: &mut GfxCtx, app: &App) {
crate::draw_with_layering(g, app, |g| self.edit.world.draw(g));
g.redraw(&self.neighborhood.fade_irrelevant);
g.redraw(&self.neighbourhood.fade_irrelevant);
self.draw_top_layer.draw(g);
self.top_panel.draw(g);
@ -174,32 +178,32 @@ impl State<App> for Viewer {
// same might be nice. And we should seed the quadtree with the locations of filters and
// arrows, possibly.
if g.canvas.is_unzoomed() {
self.neighborhood.labels.draw(g, app);
self.neighbourhood.labels.draw(g, app);
}
}
fn recreate(&mut self, ctx: &mut EventCtx, app: &mut App) -> Box<dyn State<App>> {
Self::new_state(ctx, app, self.neighborhood.id)
Self::new_state(ctx, app, self.neighbourhood.id)
}
}
fn setup_editing(
ctx: &mut EventCtx,
app: &App,
neighborhood: &Neighborhood,
) -> (EditNeighborhood, ToggleZoomed) {
neighbourhood: &Neighbourhood,
) -> (EditNeighbourhood, ToggleZoomed) {
let shortcuts = ctx.loading_screen("find shortcuts", |_, timer| {
find_shortcuts(app, neighborhood, timer)
find_shortcuts(app, neighbourhood, timer)
});
let mut edit = EditNeighborhood::new(ctx, app, neighborhood, &shortcuts);
let mut edit = EditNeighbourhood::new(ctx, app, neighbourhood, &shortcuts);
let map = &app.map;
// The world is drawn in between areas and roads, but some things need to be drawn on top of
// roads
let mut draw_top_layer = ToggleZoomed::builder();
let render_cells = crate::draw_cells::RenderCells::new(map, neighborhood);
let render_cells = crate::draw_cells::RenderCells::new(map, neighbourhood);
if app.session.draw_cells_as_areas {
edit.world.draw_master_batch(ctx, render_cells.draw());
@ -213,7 +217,7 @@ fn setup_editing(
draw_top_layer.append(colorer.draw);
} else {
for (idx, cell) in neighborhood.cells.iter().enumerate() {
for (idx, cell) in neighbourhood.cells.iter().enumerate() {
let color = render_cells.colors[idx].alpha(0.9);
for (r, interval) in &cell.roads {
let road = map.get_r(*r);
@ -233,23 +237,23 @@ fn setup_editing(
}
// Draw the borders of each cell
for (idx, cell) in neighborhood.cells.iter().enumerate() {
for (idx, cell) in neighbourhood.cells.iter().enumerate() {
let color = render_cells.colors[idx];
for i in &cell.borders {
// Most borders only have one road in the interior of the neighborhood. Draw an arrow
// Most borders only have one road in the interior of the neighbourhood. Draw an arrow
// for each of those. If there happen to be multiple interior roads for one border, the
// arrows will overlap each other -- but that happens anyway with borders close
// together at certain angles.
for r in cell.roads.keys() {
let road = map.get_r(*r);
// Design choice: when we have a filter right at the entrance of a neighborhood, it
// Design choice: when we have a filter right at the entrance of a neighbourhood, it
// creates its own little cell allowing access to just the very beginning of the
// road. Let's not draw anything for that.
if app.session.modal_filters.roads.contains_key(r) {
continue;
}
// Find the angle pointing into the neighborhood
// Find the angle pointing into the neighbourhood
let angle_in = if road.src_i == *i {
road.center_pts.first_line().angle()
} else if road.dst_i == *i {
@ -284,11 +288,11 @@ fn setup_editing(
}
// Draw one-way arrows
for r in neighborhood
for r in neighbourhood
.orig_perimeter
.interior
.iter()
.chain(neighborhood.orig_perimeter.roads.iter().map(|id| &id.road))
.chain(neighbourhood.orig_perimeter.roads.iter().map(|id| &id.road))
{
let road = map.get_r(*r);
if let Some(dir) = road.oneway_for_driving() {
@ -321,7 +325,7 @@ fn setup_editing(
fn help() -> Vec<&'static str> {
vec![
"The colored cells show where it's possible to drive without leaving the neighborhood.",
"The colored cells show where it's possible to drive without leaving the neighbourhood.",
"",
"The darker red roads have more predicted shortcutting traffic.",
"",

View File

@ -5,20 +5,20 @@ use widgetry::{
Widget,
};
use crate::{App, NeighborhoodID, Transition};
use crate::{App, NeighbourhoodID, Transition};
pub struct CustomizeBoundary {
panel: Panel,
edit: EditPolygon,
id: NeighborhoodID,
id: NeighbourhoodID,
}
impl CustomizeBoundary {
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighborhoodID) -> Box<dyn State<App>> {
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighbourhoodID) -> Box<dyn State<App>> {
let points = app
.session
.partitioning
.neighborhood_boundary_polygon(app, id)
.neighbourhood_boundary_polygon(app, id)
.into_points();
Box::new(Self {
id,
@ -53,7 +53,7 @@ impl State<App> for CustomizeBoundary {
if let Ok(ring) = Ring::new(pts) {
app.session
.partitioning
.override_neighborhood_boundary_polygon(self.id, ring.into_polygon());
.override_neighbourhood_boundary_polygon(self.id, ring.into_polygon());
return Transition::Multi(vec![Transition::Pop, Transition::Recreate]);
}
// Silently stay here so the user can try to fix

View File

@ -5,7 +5,7 @@ use map_gui::tools::Grid;
use map_model::Map;
use widgetry::{Color, GeomBatch};
use crate::{colors, Neighborhood};
use crate::{colors, Neighbourhood};
const RESOLUTION_M: f64 = 10.0;
@ -16,22 +16,22 @@ pub struct RenderCells {
}
struct RenderCellsBuilder {
/// The grid only covers the boundary polygon of the neighborhood. The values are cell indices,
/// and `Some(num_cells)` marks the boundary of the neighborhood.
/// The grid only covers the boundary polygon of the neighbourhood. The values are cell indices,
/// and `Some(num_cells)` marks the boundary of the neighbourhood.
grid: Grid<Option<usize>>,
colors: Vec<Color>,
/// Bounds of the neighborhood boundary polygon
/// Bounds of the neighbourhood boundary polygon
bounds: Bounds,
boundary_polygon: Polygon,
}
impl RenderCells {
/// Partition a neighborhood's boundary polygon based on the cells. This discretizes space into
/// Partition a neighbourhood's boundary polygon based on the cells. This discretizes space into
/// a grid, and then extracts a polygon from the raster. The results don't look perfect, but
/// it's fast.
pub fn new(map: &Map, neighborhood: &Neighborhood) -> RenderCells {
RenderCellsBuilder::new(map, neighborhood).finalize()
pub fn new(map: &Map, neighbourhood: &Neighbourhood) -> RenderCells {
RenderCellsBuilder::new(map, neighbourhood).finalize()
}
pub fn draw(&self) -> GeomBatch {
@ -55,8 +55,8 @@ impl RenderCells {
}
impl RenderCellsBuilder {
fn new(map: &Map, neighborhood: &Neighborhood) -> RenderCellsBuilder {
let boundary_polygon = neighborhood
fn new(map: &Map, neighbourhood: &Neighbourhood) -> RenderCellsBuilder {
let boundary_polygon = neighbourhood
.orig_perimeter
.clone()
.to_block(map)
@ -73,7 +73,7 @@ impl RenderCellsBuilder {
// Initially fill out the grid based on the roads in each cell
let mut warn_leak = true;
for (cell_idx, cell) in neighborhood.cells.iter().enumerate() {
for (cell_idx, cell) in neighbourhood.cells.iter().enumerate() {
for (r, interval) in &cell.roads {
let road = map.get_r(*r);
// Some roads with a filter are _very_ short, and this fails. The connecting roads
@ -91,18 +91,18 @@ impl RenderCellsBuilder {
((pt.x() - bounds.min_x) / RESOLUTION_M) as usize,
((pt.y() - bounds.min_y) / RESOLUTION_M) as usize,
);
// Due to tunnels/bridges, sometimes a road belongs to a neighborhood, but
// leaks outside the neighborhood's boundary. Avoid crashing. The real fix
// Due to tunnels/bridges, sometimes a road belongs to a neighbourhood, but
// leaks outside the neighbourhood's boundary. Avoid crashing. The real fix
// is to better define boundaries in the face of z-order changes.
//
// Example is https://www.openstreetmap.org/way/87298633
if grid_idx >= grid.data.len() {
if warn_leak {
warn!(
"{} leaks outside its neighborhood's boundary polygon, near {}",
"{} leaks outside its neighbourhood's boundary polygon, near {}",
road.id, pt
);
// In some neighborhoods, there are so many warnings that logging
// In some neighbourhoods, there are so many warnings that logging
// causes noticeable slowdown!
warn_leak = false;
}
@ -119,7 +119,7 @@ impl RenderCellsBuilder {
// Also mark the boundary polygon, so we can prevent the diffusion from "leaking" outside
// the area. The grid covers the rectangular bounds of the polygon. Rather than make an
// enum with 3 cases, just assign a new index to mean "boundary."
let boundary_marker = neighborhood.cells.len();
let boundary_marker = neighbourhood.cells.len();
for (pt, _) in
geom::PolyLine::unchecked_new(boundary_polygon.clone().into_ring().into_points())
.step_along(Distance::meters(RESOLUTION_M / 2.0), Distance::ZERO)
@ -134,10 +134,10 @@ impl RenderCellsBuilder {
}
let adjacencies = diffusion(&mut grid, boundary_marker);
let mut cell_colors = color_cells(neighborhood.cells.len(), adjacencies);
let mut cell_colors = color_cells(neighbourhood.cells.len(), adjacencies);
// Color some special cells
for (idx, cell) in neighborhood.cells.iter().enumerate() {
for (idx, cell) in neighbourhood.cells.iter().enumerate() {
if cell.is_disconnected() {
cell_colors[idx] = colors::DISCONNECTED_CELL;
}
@ -202,7 +202,7 @@ impl RenderCellsBuilder {
}
}
// Sometimes one cell "leaks" out of the neighborhood boundary. Not sure why. But we
// Sometimes one cell "leaks" out of the neighbourhood boundary. Not sure why. But we
// can just clip the result.
let mut clipped = Vec::new();
for p in cell_polygons {

View File

@ -6,7 +6,7 @@ use widgetry::{lctrl, EventCtx, Image, Key, Line, Text, TextExt, Widget};
use super::Obj;
use crate::shortcuts::Shortcuts;
use crate::{after_edit, colors, App, DiagonalFilter, Neighborhood};
use crate::{after_edit, colors, App, DiagonalFilter, Neighbourhood};
pub fn widget(ctx: &mut EventCtx, app: &App) -> Widget {
Widget::col(vec![
@ -44,13 +44,13 @@ pub fn widget(ctx: &mut EventCtx, app: &App) -> Widget {
pub fn make_world(
ctx: &mut EventCtx,
app: &App,
neighborhood: &Neighborhood,
neighbourhood: &Neighbourhood,
shortcuts: &Shortcuts,
) -> World<Obj> {
let map = &app.map;
let mut world = World::bounded(map.get_bounds());
for r in &neighborhood.orig_perimeter.interior {
for r in &neighbourhood.orig_perimeter.interior {
let road = map.get_r(*r);
world
.add(Obj::InteriorRoad(*r))
@ -67,7 +67,7 @@ pub fn make_world(
.build(ctx);
}
for i in &neighborhood.interior_intersections {
for i in &neighbourhood.interior_intersections {
world
.add(Obj::InteriorIntersection(*i))
.hitbox(map.get_i(*i).polygon.clone())

View File

@ -6,7 +6,7 @@ use widgetry::mapspace::{ObjectID, World};
use widgetry::{EventCtx, Key, Line, Panel, PanelBuilder, Widget, DEFAULT_CORNER_RADIUS};
use crate::shortcuts::Shortcuts;
use crate::{after_edit, App, BrowseNeighborhoods, Neighborhood, Transition};
use crate::{after_edit, App, BrowseNeighbourhoods, Neighbourhood, Transition};
// TODO This is only used for styling now
#[derive(PartialEq)]
@ -60,7 +60,7 @@ impl Tab {
}
}
pub struct EditNeighborhood {
pub struct EditNeighbourhood {
// Only pub for drawing
pub world: World<Obj>,
}
@ -72,7 +72,7 @@ pub enum Obj {
}
impl ObjectID for Obj {}
impl EditNeighborhood {
impl EditNeighbourhood {
pub fn temporary() -> Self {
Self {
world: World::unbounded(),
@ -82,14 +82,14 @@ impl EditNeighborhood {
pub fn new(
ctx: &mut EventCtx,
app: &App,
neighborhood: &Neighborhood,
neighbourhood: &Neighbourhood,
shortcuts: &Shortcuts,
) -> Self {
Self {
world: if app.session.edit_filters {
filters::make_world(ctx, app, neighborhood, shortcuts)
filters::make_world(ctx, app, neighbourhood, shortcuts)
} else {
one_ways::make_world(ctx, app, neighborhood)
one_ways::make_world(ctx, app, neighbourhood)
},
}
}
@ -104,8 +104,8 @@ impl EditNeighborhood {
) -> PanelBuilder {
let contents = Widget::col(vec![
app.session.alt_proposals.to_widget(ctx, app),
BrowseNeighborhoods::button(ctx, app),
Line("Editing neighborhood")
BrowseNeighbourhoods::button(ctx, app),
Line("Editing neighbourhood")
.small_heading()
.into_widget(ctx),
edit_mode(ctx, app.session.edit_filters),
@ -122,7 +122,7 @@ impl EditNeighborhood {
crate::components::LeftPanel::builder(ctx, top_panel, contents)
}
/// If true, the neighborhood has changed and the caller should recalculate stuff, including
/// If true, the neighbourhood has changed and the caller should recalculate stuff, including
/// the panel
pub fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> bool {
let outcome = self.world.event(ctx);
@ -138,14 +138,14 @@ impl EditNeighborhood {
ctx: &mut EventCtx,
app: &mut App,
action: &str,
neighborhood: &Neighborhood,
neighbourhood: &Neighbourhood,
panel: &Panel,
) -> Option<Transition> {
let id = neighborhood.id;
let id = neighbourhood.id;
match action {
"Browse neighborhoods" => {
"Browse neighbourhoods" => {
// Recalculate the state to redraw any changed filters
Some(Transition::Replace(BrowseNeighborhoods::new_state(
Some(Transition::Replace(BrowseNeighbourhoods::new_state(
ctx, app,
)))
}
@ -162,7 +162,7 @@ impl EditNeighborhood {
"Create filters along a shape" => Some(Transition::Push(
crate::components::FreehandFilters::new_state(
ctx,
neighborhood,
neighbourhood,
panel.center_of("Create filters along a shape"),
),
)),

View File

@ -5,7 +5,7 @@ use widgetry::mapspace::{World, WorldOutcome};
use widgetry::{EventCtx, Image, Text, TextExt, Widget};
use super::Obj;
use crate::{colors, App, Neighborhood};
use crate::{colors, App, Neighbourhood};
pub fn widget(ctx: &mut EventCtx) -> Widget {
Widget::col(vec![
@ -19,11 +19,11 @@ pub fn widget(ctx: &mut EventCtx) -> Widget {
])
}
pub fn make_world(ctx: &mut EventCtx, app: &App, neighborhood: &Neighborhood) -> World<Obj> {
pub fn make_world(ctx: &mut EventCtx, app: &App, neighbourhood: &Neighbourhood) -> World<Obj> {
let map = &app.map;
let mut world = World::bounded(map.get_bounds());
for r in &neighborhood.orig_perimeter.interior {
for r in &neighbourhood.orig_perimeter.interior {
let road = map.get_r(*r);
if !PathConstraints::Car.can_use_road(road, map) {
continue;

View File

@ -3,7 +3,7 @@ use anyhow::Result;
use geom::{PolyLine, Pt2D};
use widgetry::EventCtx;
use crate::{App, Neighborhood};
use crate::{App, Neighbourhood};
/// Returns the path where the file was written
pub fn write_geojson_file(ctx: &EventCtx, app: &App) -> Result<String> {
@ -19,8 +19,8 @@ fn geojson_string(ctx: &EventCtx, app: &App) -> Result<String> {
let map = &app.map;
let mut features = Vec::new();
// All neighborhood boundaries
for (id, info) in app.session.partitioning.all_neighborhoods() {
// All neighbourhood boundaries
for (id, info) in app.session.partitioning.all_neighbourhoods() {
let mut feature = Feature {
bbox: None,
geometry: Some(info.block.polygon.to_geojson(None)),
@ -28,15 +28,15 @@ fn geojson_string(ctx: &EventCtx, app: &App) -> Result<String> {
properties: None,
foreign_members: None,
};
feature.set_property("type", "neighborhood");
feature.set_property("type", "neighbourhood");
feature.set_property("fill", info.color.as_hex());
// Cells should cover these up
feature.set_property("fill-opacity", 0.0);
features.push(feature);
// Cells per neighborhood
// Cells per neighbourhood
let render_cells =
crate::draw_cells::RenderCells::new(map, &Neighborhood::new(ctx, app, *id));
crate::draw_cells::RenderCells::new(map, &Neighbourhood::new(ctx, app, *id));
for (idx, multipolygon) in render_cells.to_multipolygons().into_iter().enumerate() {
let mut feature = Feature {
bbox: None,

View File

@ -1,4 +1,4 @@
//! Experiments to make a neighborhood be low-traffic by automatically placing filters to prevent
//! Experiments to make a neighbourhood be low-traffic by automatically placing filters to prevent
//! all shortcuts.
use anyhow::Result;
@ -8,7 +8,7 @@ use map_model::RoadID;
use widgetry::{Choice, EventCtx};
use crate::shortcuts::find_shortcuts;
use crate::{after_edit, App, Neighborhood};
use crate::{after_edit, App, Neighbourhood};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Heuristic {
@ -48,17 +48,17 @@ impl Heuristic {
self,
ctx: &EventCtx,
app: &mut App,
neighborhood: &Neighborhood,
neighbourhood: &Neighbourhood,
timer: &mut Timer,
) -> Result<()> {
if neighborhood
if neighbourhood
.cells
.iter()
.filter(|c| c.is_disconnected())
.count()
!= 0
{
bail!("This neighborhood has a disconnected cell; fix that first");
bail!("This neighbourhood has a disconnected cell; fix that first");
}
// TODO If we already have no shortcuts, stop
@ -66,10 +66,10 @@ impl Heuristic {
app.session.modal_filters.before_edit();
match self {
Heuristic::Greedy => greedy(ctx, app, neighborhood, timer),
Heuristic::BruteForce => brute_force(ctx, app, neighborhood, timer),
Heuristic::SplitCells => split_cells(ctx, app, neighborhood, timer),
Heuristic::OnlyOneBorder => only_one_border(app, neighborhood),
Heuristic::Greedy => greedy(ctx, app, neighbourhood, timer),
Heuristic::BruteForce => brute_force(ctx, app, neighbourhood, timer),
Heuristic::SplitCells => split_cells(ctx, app, neighbourhood, timer),
Heuristic::OnlyOneBorder => only_one_border(app, neighbourhood),
}
let empty = app.session.modal_filters.cancel_empty_edit();
@ -82,8 +82,8 @@ impl Heuristic {
}
}
fn greedy(ctx: &EventCtx, app: &mut App, neighborhood: &Neighborhood, timer: &mut Timer) {
let shortcuts = find_shortcuts(app, &neighborhood, timer);
fn greedy(ctx: &EventCtx, app: &mut App, neighbourhood: &Neighbourhood, timer: &mut Timer) {
let shortcuts = find_shortcuts(app, &neighbourhood, timer);
// TODO How should we break ties? Some shortcuts are worse than others; use that weight?
// TODO Should this operation be per cell instead? We could hover on a road belonging to that
// cell to select it
@ -93,28 +93,28 @@ fn greedy(ctx: &EventCtx, app: &mut App, neighborhood: &Neighborhood, timer: &mu
.iter()
.max_by_key(|pair| pair.1)
{
if try_to_filter_road(ctx, app, neighborhood, *r).is_none() {
if try_to_filter_road(ctx, app, neighbourhood, *r).is_none() {
warn!("Filtering {} disconnects a cell, never mind", r);
// TODO Try the next choice
}
}
}
fn brute_force(ctx: &EventCtx, app: &mut App, neighborhood: &Neighborhood, timer: &mut Timer) {
fn brute_force(ctx: &EventCtx, app: &mut App, neighbourhood: &Neighbourhood, timer: &mut Timer) {
// Which road leads to the fewest shortcuts?
let mut best: Option<(RoadID, usize)> = None;
let orig_filters = app.session.modal_filters.roads.len();
timer.start_iter(
"evaluate candidate filters",
neighborhood.orig_perimeter.interior.len(),
neighbourhood.orig_perimeter.interior.len(),
);
for r in &neighborhood.orig_perimeter.interior {
for r in &neighbourhood.orig_perimeter.interior {
timer.next();
if app.session.modal_filters.roads.contains_key(r) {
continue;
}
if let Some(new) = try_to_filter_road(ctx, app, neighborhood, *r) {
if let Some(new) = try_to_filter_road(ctx, app, neighbourhood, *r) {
let num_shortcuts =
// This spams too many logs, and can't be used within a start_iter anyway
find_shortcuts(app, &new, &mut Timer::throwaway())
@ -132,27 +132,27 @@ fn brute_force(ctx: &EventCtx, app: &mut App, neighborhood: &Neighborhood, timer
}
if let Some((r, _)) = best {
try_to_filter_road(ctx, app, neighborhood, r).unwrap();
try_to_filter_road(ctx, app, neighbourhood, r).unwrap();
}
}
fn split_cells(ctx: &EventCtx, app: &mut App, neighborhood: &Neighborhood, timer: &mut Timer) {
fn split_cells(ctx: &EventCtx, app: &mut App, neighbourhood: &Neighbourhood, timer: &mut Timer) {
// Filtering which road leads to new cells with the MOST streets in the smaller cell?
let mut best: Option<(RoadID, usize)> = None;
let orig_filters = app.session.modal_filters.roads.len();
timer.start_iter(
"evaluate candidate filters",
neighborhood.orig_perimeter.interior.len(),
neighbourhood.orig_perimeter.interior.len(),
);
for r in &neighborhood.orig_perimeter.interior {
for r in &neighbourhood.orig_perimeter.interior {
timer.next();
if app.session.modal_filters.roads.contains_key(r) {
continue;
}
if let Some(new) = try_to_filter_road(ctx, app, neighborhood, *r) {
if let Some(new) = try_to_filter_road(ctx, app, neighbourhood, *r) {
// Did we split the cell?
if new.cells.len() > neighborhood.cells.len() {
if new.cells.len() > neighbourhood.cells.len() {
// Find the two new cells
let split_cells: Vec<_> = new
.cells
@ -178,12 +178,12 @@ fn split_cells(ctx: &EventCtx, app: &mut App, neighborhood: &Neighborhood, timer
}
if let Some((r, _)) = best {
try_to_filter_road(ctx, app, neighborhood, r).unwrap();
try_to_filter_road(ctx, app, neighbourhood, r).unwrap();
}
}
fn only_one_border(app: &mut App, neighborhood: &Neighborhood) {
for cell in &neighborhood.cells {
fn only_one_border(app: &mut App, neighbourhood: &Neighbourhood) {
for cell in &neighbourhood.cells {
if cell.borders.len() > 1 {
// TODO How to pick which one to leave open?
for i in cell.borders.iter().skip(1) {
@ -209,25 +209,25 @@ fn only_one_border(app: &mut App, neighborhood: &Neighborhood) {
}
}
// If successful, returns a Neighborhood and leaves the new filter in place. If it disconncts a
// If successful, returns a Neighbourhood and leaves the new filter in place. If it disconncts a
// cell, reverts the change and returns None
fn try_to_filter_road(
ctx: &EventCtx,
app: &mut App,
neighborhood: &Neighborhood,
neighbourhood: &Neighbourhood,
r: RoadID,
) -> Option<Neighborhood> {
) -> Option<Neighbourhood> {
let road = app.map.get_r(r);
app.session
.modal_filters
.roads
.insert(r, road.length() / 2.0);
// TODO This is expensive; can we just do the connectivity work and not drawing?
let new_neighborhood = Neighborhood::new(ctx, app, neighborhood.id);
if new_neighborhood.cells.iter().any(|c| c.is_disconnected()) {
let new_neighbourhood = Neighbourhood::new(ctx, app, neighbourhood.id);
if new_neighbourhood.cells.iter().any(|c| c.is_disconnected()) {
app.session.modal_filters.roads.remove(&r).unwrap();
None
} else {
Some(new_neighborhood)
Some(new_neighbourhood)
}
}

View File

@ -10,7 +10,7 @@ use widgetry::{
};
use crate::impact::{end_of_day, Filters, Impact};
use crate::{colors, App, BrowseNeighborhoods, Transition};
use crate::{colors, App, BrowseNeighbourhoods, Transition};
// TODO Share structure or pieces with Ungap's predict mode
// ... can't we just produce data of a certain shape, and have a UI pretty tuned for that?
@ -47,7 +47,7 @@ impl ShowResults {
}
let contents = Widget::col(vec![
BrowseNeighborhoods::button(ctx, app),
BrowseNeighbourhoods::button(ctx, app),
Line("Impact prediction").small_heading().into_widget(ctx),
Text::from(Line("This tool starts with a travel demand model, calculates the route every trip takes before and after changes, and displays volumes along roads")).wrap_to_pct(ctx, 20).into_widget(ctx),
Text::from_all(vec![
@ -79,10 +79,10 @@ impl State<App> for ShowResults {
}
match self.left_panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"Browse neighborhoods" => {
"Browse neighbourhoods" => {
// Don't just Pop; if we updated the results, the UI won't warn the user about a slow
// loading
return Transition::Replace(BrowseNeighborhoods::new_state(ctx, app));
return Transition::Replace(BrowseNeighbourhoods::new_state(ctx, app));
}
"Save before/after counts to files" => {
let path1 = "counts_a.json";

View File

@ -6,11 +6,11 @@ use abstio::MapName;
use abstutil::Timer;
use widgetry::{EventCtx, GfxCtx, Settings};
pub use browse::BrowseNeighborhoods;
pub use browse::BrowseNeighbourhoods;
use filters::Toggle3Zoomed;
pub use filters::{DiagonalFilter, ModalFilters};
pub use neighborhood::{Cell, DistanceInterval, Neighborhood};
pub use partition::{NeighborhoodID, Partitioning};
pub use neighbourhood::{Cell, DistanceInterval, Neighbourhood};
pub use partition::{NeighbourhoodID, Partitioning};
#[macro_use]
extern crate anyhow;
@ -27,7 +27,7 @@ mod edit;
mod export;
mod filters;
mod impact;
mod neighborhood;
mod neighbourhood;
mod partition;
mod route_planner;
mod save;
@ -39,7 +39,7 @@ type App = map_gui::SimpleApp<Session>;
type Transition = widgetry::Transition<App>;
pub fn main() {
let settings = Settings::new("Low traffic neighborhoods");
let settings = Settings::new("Low traffic neighbourhoods");
run(settings);
}
@ -48,7 +48,7 @@ struct Args {
/// Load a previously saved proposal with this name. Note this takes a name, not a full path.
#[structopt(long)]
proposal: Option<String>,
/// Lock the user into one fixed neighborhood, and remove many controls
/// Lock the user into one fixed neighbourhood, and remove many controls
#[structopt(long)]
consultation: Option<String>,
#[structopt(flatten)]
@ -87,7 +87,7 @@ fn run(mut settings: Settings) {
edit_filters: true,
highlight_boundary_roads: false,
draw_neighborhood_style: browse::Style::SimpleColoring,
draw_neighbourhood_style: browse::Style::SimpleColoring,
draw_cells_as_areas: true,
heuristic: filters::auto::Heuristic::SplitCells,
main_road_penalty: 1.0,
@ -103,7 +103,7 @@ fn run(mut settings: Settings) {
args.app_args.cam,
session,
move |ctx, app| {
// Restore the partitioning from a file before calling BrowseNeighborhoods
// Restore the partitioning from a file before calling BrowseNeighbourhoods
let popup_state = args.proposal.as_ref().and_then(|name| {
crate::save::Proposal::load(
ctx,
@ -147,7 +147,7 @@ fn run(mut settings: Settings) {
_ => panic!("Unknown Bristol consultation mode {consultation}"),
};
// Look for the neighborhood containing one small street
// Look for the neighbourhood containing one small street
let r = app
.map
.all_roads()
@ -155,16 +155,16 @@ fn run(mut settings: Settings) {
.find(|r| r.get_name(None) == focus_on_street)
.expect(&format!("Can't find {focus_on_street}"))
.id;
let (neighborhood, _) = app
let (neighbourhood, _) = app
.session
.partitioning
.all_neighborhoods()
.all_neighbourhoods()
.iter()
.find(|(_, info)| info.block.perimeter.interior.contains(&r))
.expect(&format!(
"Can't find neighborhood containing {focus_on_street}"
"Can't find neighbourhood containing {focus_on_street}"
));
app.session.consultation = Some(*neighborhood);
app.session.consultation = Some(*neighbourhood);
// TODO Maybe center the camera, ignoring any saved values
@ -178,9 +178,9 @@ fn run(mut settings: Settings) {
ctx,
app,
map_gui::tools::Executable::LTN,
Box::new(|ctx, app, _| BrowseNeighborhoods::new_state(ctx, app)),
Box::new(|ctx, app, _| BrowseNeighbourhoods::new_state(ctx, app)),
));
states.push(BrowseNeighborhoods::new_state(ctx, app));
states.push(BrowseNeighbourhoods::new_state(ctx, app));
}
if let Some(state) = popup_state {
states.push(state);
@ -197,7 +197,7 @@ use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(js_name = "run")]
pub fn run_wasm(root_dom_id: String, assets_base_url: String, assets_are_gzipped: bool) {
let settings = Settings::new("Low traffic neighborhoods")
let settings = Settings::new("Low traffic neighbourhoods")
.root_dom_element_id(root_dom_id)
.assets_base_url(assets_base_url)
.assets_are_gzipped(assets_are_gzipped);
@ -221,9 +221,9 @@ pub struct Session {
pub edit_filters: bool,
// Remember form settings in different tabs.
// Browse neighborhoods:
// Browse neighbourhoods:
pub highlight_boundary_roads: bool,
pub draw_neighborhood_style: browse::Style,
pub draw_neighbourhood_style: browse::Style,
// Connectivity:
pub draw_cells_as_areas: bool,
pub heuristic: filters::auto::Heuristic,
@ -232,7 +232,7 @@ pub struct Session {
current_trip_name: Option<String>,
consultation: Option<NeighborhoodID>,
consultation: Option<NeighbourhoodID>,
}
/// Do the equivalent of `SimpleApp::draw_unzoomed` or `draw_zoomed`, but after the water/park

View File

@ -7,10 +7,10 @@ use map_gui::tools::DrawRoadLabels;
use map_model::{IntersectionID, Map, PathConstraints, Perimeter, RoadID};
use widgetry::{Drawable, EventCtx, GeomBatch};
use crate::{App, ModalFilters, NeighborhoodID};
use crate::{App, ModalFilters, NeighbourhoodID};
pub struct Neighborhood {
pub id: NeighborhoodID,
pub struct Neighbourhood {
pub id: NeighbourhoodID,
// These're fixed
pub orig_perimeter: Perimeter,
@ -18,7 +18,7 @@ pub struct Neighborhood {
pub borders: BTreeSet<IntersectionID>,
pub interior_intersections: BTreeSet<IntersectionID>,
// The cells change as a result of modal filters, which're stored for all neighborhoods in
// The cells change as a result of modal filters, which're stored for all neighbourhoods in
// app.session.
pub cells: Vec<Cell>,
@ -26,13 +26,13 @@ pub struct Neighborhood {
pub labels: DrawRoadLabels,
}
/// A partitioning of the interior of a neighborhood based on driving connectivity
/// A partitioning of the interior of a neighbourhood based on driving connectivity
pub struct Cell {
/// Most roads are fully in one cell. Roads with modal filters on them are sometimes split
/// between two cells, and the DistanceInterval indicates the split. The distances are over the
/// road's center line length.
pub roads: BTreeMap<RoadID, DistanceInterval>,
/// Intersections where this cell touches the boundary of the neighborhood.
/// Intersections where this cell touches the boundary of the neighbourhood.
pub borders: BTreeSet<IntersectionID>,
}
@ -49,17 +49,17 @@ pub struct DistanceInterval {
pub end: Distance,
}
impl Neighborhood {
pub fn new(ctx: &EventCtx, app: &App, id: NeighborhoodID) -> Neighborhood {
impl Neighbourhood {
pub fn new(ctx: &EventCtx, app: &App, id: NeighbourhoodID) -> Neighbourhood {
let map = &app.map;
let orig_perimeter = app
.session
.partitioning
.neighborhood_block(id)
.neighbourhood_block(id)
.perimeter
.clone();
let mut n = Neighborhood {
let mut n = Neighbourhood {
id,
orig_perimeter,
perimeter: BTreeSet::new(),
@ -84,7 +84,7 @@ impl Neighborhood {
vec![app
.session
.partitioning
.neighborhood_boundary_polygon(app, id)
.neighbourhood_boundary_polygon(app, id)
.into_ring()],
);
n.fade_irrelevant = GeomBatch::from(vec![(app.cs.fade_map_dark, fade_area)]).upload(ctx);
@ -178,7 +178,7 @@ fn find_cells(
fn floodfill(
map: &Map,
start: RoadID,
neighborhood_borders: &BTreeSet<IntersectionID>,
neighbourhood_borders: &BTreeSet<IntersectionID>,
modal_filters: &ModalFilters,
) -> Cell {
let mut visited_roads: BTreeMap<RoadID, DistanceInterval> = BTreeMap::new();
@ -204,10 +204,10 @@ fn floodfill(
);
for i in [current.src_i, current.dst_i] {
// It's possible for one border intersection to have two roads in the interior of the
// neighborhood. Don't consider a turn between those roads through this intersection as
// neighbourhood. Don't consider a turn between those roads through this intersection as
// counting as connectivity -- we're right at the boundary road, so it's like leaving
// and re-entering the neighborhood.
if neighborhood_borders.contains(&i) {
// and re-entering the neighbourhood.
if neighbourhood_borders.contains(&i) {
cell_borders.insert(i);
continue;
}

View File

@ -14,41 +14,41 @@ use crate::{colors, App};
/// An opaque ID, won't be contiguous as we adjust boundaries
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct NeighborhoodID(pub usize);
pub struct NeighbourhoodID(pub usize);
/// Identifies a single / unmerged block, which never changes
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct BlockID(usize);
// Some states want this
impl widgetry::mapspace::ObjectID for NeighborhoodID {}
impl widgetry::mapspace::ObjectID for NeighbourhoodID {}
impl widgetry::mapspace::ObjectID for BlockID {}
#[derive(Clone, Serialize, Deserialize)]
pub struct Partitioning {
pub map: MapName,
neighborhoods: BTreeMap<NeighborhoodID, NeighborhoodInfo>,
neighbourhoods: BTreeMap<NeighbourhoodID, NeighbourhoodInfo>,
// The single / unmerged blocks never change
single_blocks: Vec<Block>,
neighborhood_id_counter: usize,
neighbourhood_id_counter: usize,
// Invariant: This is a surjection, every block belongs to exactly one neighborhood
block_to_neighborhood: BTreeMap<BlockID, NeighborhoodID>,
// Invariant: This is a surjection, every block belongs to exactly one neighbourhood
block_to_neighbourhood: BTreeMap<BlockID, NeighbourhoodID>,
use_expensive_blockfinding: bool,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct NeighborhoodInfo {
pub struct NeighbourhoodInfo {
pub block: Block,
pub color: Color,
/// Draw a special cone of light when focused on this neighborhood. It doesn't change which
/// Draw a special cone of light when focused on this neighbourhood. It doesn't change which
/// roads can be edited.
pub override_drawing_boundary: Option<Polygon>,
}
impl NeighborhoodInfo {
impl NeighbourhoodInfo {
fn new(block: Block) -> Self {
Self {
block,
@ -63,19 +63,19 @@ impl Partitioning {
pub fn empty() -> Partitioning {
Partitioning {
map: MapName::new("zz", "temp", "orary"),
neighborhoods: BTreeMap::new(),
neighbourhoods: BTreeMap::new(),
single_blocks: Vec::new(),
neighborhood_id_counter: 0,
neighbourhood_id_counter: 0,
block_to_neighborhood: BTreeMap::new(),
block_to_neighbourhood: BTreeMap::new(),
use_expensive_blockfinding: false,
}
}
pub fn is_empty(&self) -> bool {
self.neighborhoods.is_empty()
self.neighbourhoods.is_empty()
}
pub fn seed_using_heuristics(app: &App, timer: &mut Timer) -> Partitioning {
@ -99,7 +99,7 @@ impl Partitioning {
timer.start("partition");
let partitions = Perimeter::partition_by_predicate(single_block_perims, |r| {
// "Interior" roads of a neighborhood aren't classified as arterial
// "Interior" roads of a neighbourhood aren't classified as arterial
map.get_r(r).get_rank() == RoadRank::Local
});
@ -130,40 +130,40 @@ impl Partitioning {
}
}
let mut neighborhoods = BTreeMap::new();
let mut neighbourhoods = BTreeMap::new();
for block in blocks {
neighborhoods.insert(
NeighborhoodID(neighborhoods.len()),
NeighborhoodInfo::new(block),
neighbourhoods.insert(
NeighbourhoodID(neighbourhoods.len()),
NeighbourhoodInfo::new(block),
);
}
let neighborhood_id_counter = neighborhoods.len();
let neighbourhood_id_counter = neighbourhoods.len();
let mut p = Partitioning {
map: map.get_name().clone(),
neighborhoods,
neighbourhoods,
single_blocks,
neighborhood_id_counter,
block_to_neighborhood: BTreeMap::new(),
neighbourhood_id_counter,
block_to_neighbourhood: BTreeMap::new(),
use_expensive_blockfinding,
};
// TODO We could probably build this up as we go
for id in p.all_block_ids() {
if let Some(neighborhood) = p.neighborhood_containing(id) {
p.block_to_neighborhood.insert(id, neighborhood);
if let Some(neighbourhood) = p.neighbourhood_containing(id) {
p.block_to_neighbourhood.insert(id, neighbourhood);
} else {
if !use_expensive_blockfinding {
// Try the expensive check, then
error!(
"Block doesn't belong to any neighborhood? Retrying with expensive checks {:?}",
"Block doesn't belong to any neighbourhood? Retrying with expensive checks {:?}",
p.get_block(id).perimeter
);
continue 'METHOD;
}
// This will break everything downstream, so bail out immediately
panic!(
"Block doesn't belong to any neighborhood?! {:?}",
"Block doesn't belong to any neighbourhood?! {:?}",
p.get_block(id).perimeter
);
}
@ -178,18 +178,25 @@ impl Partitioning {
/// True if the coloring changed
pub fn recalculate_coloring(&mut self) -> bool {
let perims: Vec<Perimeter> = self
.neighborhoods
.neighbourhoods
.values()
.map(|info| info.block.perimeter.clone())
.collect();
let colors = Perimeter::calculate_coloring(&perims, colors::NEIGHBORHOODS.len())
let colors = Perimeter::calculate_coloring(&perims, colors::NEIGHBOURHOODS.len())
.unwrap_or_else(|| (0..perims.len()).collect());
let orig_coloring: Vec<Color> =
self.neighborhoods.values().map(|info| info.color).collect();
for (info, color_idx) in self.neighborhoods.values_mut().zip(colors.into_iter()) {
info.color = colors::NEIGHBORHOODS[color_idx % colors::NEIGHBORHOODS.len()];
let orig_coloring: Vec<Color> = self
.neighbourhoods
.values()
.map(|info| info.color)
.collect();
for (info, color_idx) in self.neighbourhoods.values_mut().zip(colors.into_iter()) {
info.color = colors::NEIGHBOURHOODS[color_idx % colors::NEIGHBOURHOODS.len()];
}
let new_coloring: Vec<Color> = self.neighborhoods.values().map(|info| info.color).collect();
let new_coloring: Vec<Color> = self
.neighbourhoods
.values()
.map(|info| info.color)
.collect();
orig_coloring != new_coloring
}
@ -198,38 +205,38 @@ impl Partitioning {
&mut self,
map: &Map,
id: BlockID,
old_owner: NeighborhoodID,
new_owner: NeighborhoodID,
) -> Result<Option<NeighborhoodID>> {
old_owner: NeighbourhoodID,
new_owner: NeighbourhoodID,
) -> Result<Option<NeighbourhoodID>> {
assert_ne!(old_owner, new_owner);
// Is the newly expanded neighborhood a valid perimeter?
// Is the newly expanded neighbourhood a valid perimeter?
let new_owner_blocks: Vec<BlockID> = self
.block_to_neighborhood
.block_to_neighbourhood
.iter()
.filter_map(|(block, neighborhood)| {
if *neighborhood == new_owner || *block == id {
.filter_map(|(block, neighbourhood)| {
if *neighbourhood == new_owner || *block == id {
Some(*block)
} else {
None
}
})
.collect();
let mut new_neighborhood_blocks = self.make_merged_blocks(map, new_owner_blocks)?;
if new_neighborhood_blocks.len() != 1 {
let mut new_neighbourhood_blocks = self.make_merged_blocks(map, new_owner_blocks)?;
if new_neighbourhood_blocks.len() != 1 {
// This happens when a hole would be created by adding this block. There are probably
// some smaller blocks nearby to add first.
bail!("Couldn't add block -- you may need to add an intermediate block first to avoid a hole, or there's a bug you can't workaround yet");
}
let new_neighborhood_block = new_neighborhood_blocks.pop().unwrap();
let new_neighbourhood_block = new_neighbourhood_blocks.pop().unwrap();
// Is the old neighborhood, minus this block, still valid?
// TODO refactor Neighborhood to BlockIDs?
// Is the old neighbourhood, minus this block, still valid?
// TODO refactor Neighbourhood to BlockIDs?
let old_owner_blocks: Vec<BlockID> = self
.block_to_neighborhood
.block_to_neighbourhood
.iter()
.filter_map(|(block, neighborhood)| {
if *neighborhood == old_owner && *block != id {
.filter_map(|(block, neighbourhood)| {
if *neighbourhood == old_owner && *block != id {
Some(*block)
} else {
None
@ -237,55 +244,56 @@ impl Partitioning {
})
.collect();
if old_owner_blocks.is_empty() {
// We're deleting the old neighborhood!
self.neighborhoods.get_mut(&new_owner).unwrap().block = new_neighborhood_block;
self.neighborhoods.remove(&old_owner).unwrap();
self.block_to_neighborhood.insert(id, new_owner);
// Tell the caller to recreate this SelectBoundary state, switching to the neighborhood
// We're deleting the old neighbourhood!
self.neighbourhoods.get_mut(&new_owner).unwrap().block = new_neighbourhood_block;
self.neighbourhoods.remove(&old_owner).unwrap();
self.block_to_neighbourhood.insert(id, new_owner);
// Tell the caller to recreate this SelectBoundary state, switching to the neighbourhood
// we just donated to, since the old is now gone
return Ok(Some(new_owner));
}
let mut old_neighborhood_blocks = self.make_merged_blocks(map, old_owner_blocks.clone())?;
// We might be splitting the old neighborhood into multiple pieces! Pick the largest piece
// as the old_owner (so the UI for trimming a neighborhood is less jarring), and create new
// neighborhoods for the others.
old_neighborhood_blocks.sort_by_key(|block| block.perimeter.interior.len());
self.neighborhoods.get_mut(&old_owner).unwrap().block =
old_neighborhood_blocks.pop().unwrap();
let new_splits = !old_neighborhood_blocks.is_empty();
for split_piece in old_neighborhood_blocks {
let new_neighborhood = NeighborhoodID(self.neighborhood_id_counter);
self.neighborhood_id_counter += 1;
let mut old_neighbourhood_blocks =
self.make_merged_blocks(map, old_owner_blocks.clone())?;
// We might be splitting the old neighbourhood into multiple pieces! Pick the largest piece
// as the old_owner (so the UI for trimming a neighbourhood is less jarring), and create new
// neighbourhoods for the others.
old_neighbourhood_blocks.sort_by_key(|block| block.perimeter.interior.len());
self.neighbourhoods.get_mut(&old_owner).unwrap().block =
old_neighbourhood_blocks.pop().unwrap();
let new_splits = !old_neighbourhood_blocks.is_empty();
for split_piece in old_neighbourhood_blocks {
let new_neighbourhood = NeighbourhoodID(self.neighbourhood_id_counter);
self.neighbourhood_id_counter += 1;
// Temporary color
self.neighborhoods
.insert(new_neighborhood, NeighborhoodInfo::new(split_piece));
self.neighbourhoods
.insert(new_neighbourhood, NeighbourhoodInfo::new(split_piece));
}
if new_splits {
// We need to update the owner of all single blocks in these new pieces
for id in old_owner_blocks {
self.block_to_neighborhood
.insert(id, self.neighborhood_containing(id).unwrap());
self.block_to_neighbourhood
.insert(id, self.neighbourhood_containing(id).unwrap());
}
}
self.neighborhoods.get_mut(&new_owner).unwrap().block = new_neighborhood_block;
self.block_to_neighborhood.insert(id, new_owner);
self.neighbourhoods.get_mut(&new_owner).unwrap().block = new_neighbourhood_block;
self.block_to_neighbourhood.insert(id, new_owner);
Ok(None)
}
/// Needs to find an existing neighborhood to take the block, or make a new one
pub fn remove_block_from_neighborhood(
/// Needs to find an existing neighbourhood to take the block, or make a new one
pub fn remove_block_from_neighbourhood(
&mut self,
map: &Map,
id: BlockID,
old_owner: NeighborhoodID,
) -> Result<Option<NeighborhoodID>> {
// Find all RoadSideIDs in the block matching the current neighborhood perimeter. Look for
// the first one that borders another neighborhood, and transfer the block there.
old_owner: NeighbourhoodID,
) -> Result<Option<NeighbourhoodID>> {
// Find all RoadSideIDs in the block matching the current neighbourhood perimeter. Look for
// the first one that borders another neighbourhood, and transfer the block there.
// TODO This can get unintuitive -- if we remove a block bordering two other
// neighborhoods, which one should we donate to?
let current_perim_set: BTreeSet<RoadSideID> = self.neighborhoods[&old_owner]
// neighbourhoods, which one should we donate to?
let current_perim_set: BTreeSet<RoadSideID> = self.neighbourhoods[&old_owner]
.block
.perimeter
.roads
@ -296,11 +304,11 @@ impl Partitioning {
if !current_perim_set.contains(road_side) {
continue;
}
// Is there another neighborhood that has the other side of this road on its perimeter?
// TODO We could map road -> BlockID then use block_to_neighborhood
// Is there another neighbourhood that has the other side of this road on its perimeter?
// TODO We could map road -> BlockID then use block_to_neighbourhood
let other_side = road_side.other_side();
if let Some((new_owner, _)) = self
.neighborhoods
.neighbourhoods
.iter()
.find(|(_, info)| info.block.perimeter.roads.contains(&other_side))
{
@ -310,16 +318,18 @@ impl Partitioning {
}
// We didn't find any match, so we're jettisoning a block near the edge of the map (or a
// buggy area missing blocks). Create a new neighborhood with just this block.
let new_owner = NeighborhoodID(self.neighborhood_id_counter);
self.neighborhood_id_counter += 1;
// buggy area missing blocks). Create a new neighbourhood with just this block.
let new_owner = NeighbourhoodID(self.neighbourhood_id_counter);
self.neighbourhood_id_counter += 1;
// Temporary color
self.neighborhoods
.insert(new_owner, NeighborhoodInfo::new(self.get_block(id).clone()));
self.neighbourhoods.insert(
new_owner,
NeighbourhoodInfo::new(self.get_block(id).clone()),
);
let result = self.transfer_block(map, id, old_owner, new_owner);
if result.is_err() {
// Revert the change above!
self.neighborhoods.remove(&new_owner).unwrap();
self.neighbourhoods.remove(&new_owner).unwrap();
}
result
}
@ -327,26 +337,26 @@ impl Partitioning {
// Read-only
impl Partitioning {
pub fn neighborhood_block(&self, id: NeighborhoodID) -> &Block {
&self.neighborhoods[&id].block
pub fn neighbourhood_block(&self, id: NeighbourhoodID) -> &Block {
&self.neighbourhoods[&id].block
}
pub fn neighborhood_area_km2(&self, id: NeighborhoodID) -> String {
pub fn neighbourhood_area_km2(&self, id: NeighbourhoodID) -> String {
// Convert from m^2 to km^2
let area = self.neighborhood_block(id).polygon.area() / 1_000_000.0;
let area = self.neighbourhood_block(id).polygon.area() / 1_000_000.0;
format!("~{:.1} km²", area)
}
pub fn neighborhood_color(&self, id: NeighborhoodID) -> Color {
self.neighborhoods[&id].color
pub fn neighbourhood_color(&self, id: NeighbourhoodID) -> Color {
self.neighbourhoods[&id].color
}
pub fn neighborhood_boundary_polygon(&self, app: &App, id: NeighborhoodID) -> Polygon {
let info = &self.neighborhoods[&id];
pub fn neighbourhood_boundary_polygon(&self, app: &App, id: NeighbourhoodID) -> Polygon {
let info = &self.neighbourhoods[&id];
if let Some(polygon) = info.override_drawing_boundary.clone() {
return polygon;
}
// The neighborhood's perimeter hugs the "interior" of the neighborhood. If we just use the
// The neighbourhood's perimeter hugs the "interior" of the neighbourhood. If we just use the
// other side of the perimeter road, the highlighted area nicely shows the boundary road
// too. (But sometimes this breaks, of course)
match info
@ -360,22 +370,26 @@ impl Partitioning {
Err(_) => info.block.polygon.clone(),
}
}
pub fn override_neighborhood_boundary_polygon(&mut self, id: NeighborhoodID, polygon: Polygon) {
self.neighborhoods
pub fn override_neighbourhood_boundary_polygon(
&mut self,
id: NeighbourhoodID,
polygon: Polygon,
) {
self.neighbourhoods
.get_mut(&id)
.unwrap()
.override_drawing_boundary = Some(polygon);
}
pub fn all_neighborhoods(&self) -> &BTreeMap<NeighborhoodID, NeighborhoodInfo> {
&self.neighborhoods
pub fn all_neighbourhoods(&self) -> &BTreeMap<NeighbourhoodID, NeighbourhoodInfo> {
&self.neighbourhoods
}
// Just used for initial creation
fn neighborhood_containing(&self, find_block: BlockID) -> Option<NeighborhoodID> {
fn neighbourhood_containing(&self, find_block: BlockID) -> Option<NeighbourhoodID> {
// TODO We could probably build this mapping up when we do Perimeter::merge_all
let find_block = self.get_block(find_block);
for (id, info) in &self.neighborhoods {
for (id, info) in &self.neighbourhoods {
if info.block.perimeter.contains(&find_block.perimeter) {
return Some(*id);
}
@ -399,13 +413,13 @@ impl Partitioning {
&self.single_blocks[id.0]
}
pub fn block_to_neighborhood(&self, id: BlockID) -> NeighborhoodID {
self.block_to_neighborhood[&id]
pub fn block_to_neighbourhood(&self, id: BlockID) -> NeighbourhoodID {
self.block_to_neighbourhood[&id]
}
pub fn all_blocks_in_neighborhood(&self, id: NeighborhoodID) -> Vec<BlockID> {
pub fn all_blocks_in_neighbourhood(&self, id: NeighbourhoodID) -> Vec<BlockID> {
let mut result = Vec::new();
for (block, n) in &self.block_to_neighborhood {
for (block, n) in &self.block_to_neighbourhood {
if *n == id {
result.push(*block);
}
@ -413,9 +427,9 @@ impl Partitioning {
result
}
pub fn some_block_in_neighborhood(&self, id: NeighborhoodID) -> BlockID {
for (block, neighborhood) in &self.block_to_neighborhood {
if id == *neighborhood {
pub fn some_block_in_neighbourhood(&self, id: NeighbourhoodID) -> BlockID {
for (block, neighbourhood) in &self.block_to_neighbourhood {
if id == *neighbourhood {
return *block;
}
}

View File

@ -10,7 +10,7 @@ use widgetry::{
RoundedF64, Spinner, State, Text, Widget,
};
use crate::{colors, App, BrowseNeighborhoods, Transition};
use crate::{colors, App, BrowseNeighbourhoods, Transition};
pub struct RoutePlanner {
top_panel: Panel,
@ -76,7 +76,7 @@ impl RoutePlanner {
let contents = Widget::col(vec![
app.session.alt_proposals.to_widget(ctx, app),
BrowseNeighborhoods::button(ctx, app),
BrowseNeighbourhoods::button(ctx, app),
ctx.style()
.btn_back("Analyze neighbourhood")
.hotkey(Key::Escape)
@ -119,9 +119,9 @@ impl RoutePlanner {
panel.restore(ctx, &self.left_panel);
self.left_panel = panel;
// Fade all neighborhood interiors, so it's very clear when a route cuts through
// Fade all neighbourhood interiors, so it's very clear when a route cuts through
let mut batch = GeomBatch::new();
for info in app.session.partitioning.all_neighborhoods().values() {
for info in app.session.partitioning.all_neighbourhoods().values() {
batch.push(app.cs.fade_map_dark, info.block.polygon.clone());
}
@ -265,8 +265,8 @@ impl State<App> for RoutePlanner {
let panel_outcome = self.left_panel.event(ctx);
if let Outcome::Clicked(ref x) = panel_outcome {
if x == "Browse neighborhoods" {
return Transition::Replace(BrowseNeighborhoods::new_state(ctx, app));
if x == "Browse neighbourhoods" {
return Transition::Replace(BrowseNeighbourhoods::new_state(ctx, app));
}
if x == "Analyze neighbourhood" {
return Transition::Pop;
@ -326,7 +326,7 @@ fn help() -> Vec<&'static str> {
vec![
"You can test how different driving routes are affected by proposed LTNs.",
"",
"The fastest route may not cut through neighborhoods normally,",
"The fastest route may not cut through neighbourhoods normally,",
"but you can adjust the slow-down factor to mimic rush hour conditions",
]
}

View File

@ -11,7 +11,7 @@ use widgetry::tools::PopupMsg;
use widgetry::{Choice, EventCtx, Key, Line, State, Widget};
use crate::partition::BlockID;
use crate::{App, BrowseNeighborhoods, ModalFilters, Partitioning, Transition};
use crate::{App, BrowseNeighbourhoods, ModalFilters, Partitioning, Transition};
/// Captures all of the edits somebody makes to a map in the LTN tool. Note this is separate from
/// `map_model::MapEdits`.
@ -307,11 +307,11 @@ impl AltProposals {
// After switching proposals, we have to recreate state
//
// To preserve per-neigbhorhood states, we have to transform neighborhood IDs, which may change if
// To preserve per-neigbhorhood states, we have to transform neighbourhood IDs, which may change if
// the partitioning is different. If the boundary is a bit different, match up by all the blocks in
// the current neighborhood.
// the current neighbourhood.
pub enum PreserveState {
BrowseNeighborhoods,
BrowseNeighbourhoods,
Route,
Connectivity(Vec<BlockID>),
Shortcuts(Option<PathRequest>, Vec<BlockID>),
@ -320,18 +320,18 @@ pub enum PreserveState {
impl PreserveState {
fn switch_to_state(self, ctx: &mut EventCtx, app: &mut App) -> Transition {
match self {
PreserveState::BrowseNeighborhoods => {
Transition::Replace(BrowseNeighborhoods::new_state(ctx, app))
PreserveState::BrowseNeighbourhoods => {
Transition::Replace(BrowseNeighbourhoods::new_state(ctx, app))
}
PreserveState::Route => {
Transition::Replace(crate::route_planner::RoutePlanner::new_state(ctx, app))
}
PreserveState::Connectivity(blocks) => {
// Count which new neighborhoods have the blocks from the original. Pick the one
// Count which new neighbourhoods have the blocks from the original. Pick the one
// with the most matches
let mut count = Counter::new();
for block in blocks {
count.inc(app.session.partitioning.block_to_neighborhood(block));
count.inc(app.session.partitioning.block_to_neighbourhood(block));
}
Transition::Replace(crate::connectivity::Viewer::new_state(
ctx,
@ -342,7 +342,7 @@ impl PreserveState {
PreserveState::Shortcuts(req, blocks) => {
let mut count = Counter::new();
for block in blocks {
count.inc(app.session.partitioning.block_to_neighborhood(block));
count.inc(app.session.partitioning.block_to_neighbourhood(block));
}
Transition::Replace(crate::shortcut_viewer::BrowseShortcuts::new_state(
ctx,

View File

@ -78,8 +78,8 @@ fn is_road_id(path: &str) -> bool {
Regex::new(r"^/partitioning/single_blocks/\d+/perimeter/interior/\d+$").unwrap(),
Regex::new(r"^/partitioning/single_blocks/\d+/perimeter/roads/\d+/road$").unwrap(),
// The other
Regex::new(r"^/partitioning/neighborhoods/\d+/0/perimeter/interior/\d+$").unwrap(),
Regex::new(r"^/partitioning/neighborhoods/\d+/0/perimeter/roads/\d+/road$").unwrap(),
Regex::new(r"^/partitioning/neighbourhoods/\d+/0/perimeter/interior/\d+$").unwrap(),
Regex::new(r"^/partitioning/neighbourhoods/\d+/0/perimeter/roads/\d+/road$").unwrap(),
];
}

View File

@ -13,12 +13,12 @@ use widgetry::{
use crate::browse::draw_boundary_roads;
use crate::partition::BlockID;
use crate::{colors, App, NeighborhoodID, Partitioning, Transition};
use crate::{colors, App, NeighbourhoodID, Partitioning, Transition};
pub struct SelectBoundary {
top_panel: Panel,
left_panel: Panel,
id: NeighborhoodID,
id: NeighbourhoodID,
world: World<BlockID>,
draw_boundary_roads: ToggleZoomed,
frontier: BTreeSet<BlockID>,
@ -35,7 +35,7 @@ pub struct SelectBoundary {
}
impl SelectBoundary {
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighborhoodID) -> Box<dyn State<App>> {
pub fn new_state(ctx: &mut EventCtx, app: &App, id: NeighbourhoodID) -> Box<dyn State<App>> {
let top_panel = crate::components::TopPanel::panel(ctx, app);
let left_panel = make_panel(ctx, app, id, &top_panel);
let mut state = SelectBoundary {
@ -54,7 +54,7 @@ impl SelectBoundary {
lasso: None,
};
let initial_boundary = app.session.partitioning.neighborhood_block(id);
let initial_boundary = app.session.partitioning.neighbourhood_block(id);
state.frontier = app
.session
.partitioning
@ -104,7 +104,7 @@ impl SelectBoundary {
}
}
// If the block is part of the current neighborhood, remove it. Otherwise add it. It's assumed
// If the block is part of the current neighbourhood, remove it. Otherwise add it. It's assumed
// this block is in the previous frontier
fn toggle_block(&mut self, ctx: &mut EventCtx, app: &mut App, id: BlockID) -> Transition {
if self.last_failed_change == Some((id, self.currently_have_block(app, id))) {
@ -113,16 +113,16 @@ impl SelectBoundary {
self.last_failed_change = None;
match self.try_toggle_block(app, id) {
Ok(Some(new_neighborhood)) => {
Ok(Some(new_neighbourhood)) => {
app.session.partitioning.recalculate_coloring();
return Transition::Replace(SelectBoundary::new_state(ctx, app, new_neighborhood));
return Transition::Replace(SelectBoundary::new_state(ctx, app, new_neighbourhood));
}
Ok(None) => {
let old_frontier = std::mem::take(&mut self.frontier);
self.frontier = app.session.partitioning.calculate_frontier(
&app.session
.partitioning
.neighborhood_block(self.id)
.neighbourhood_block(self.id)
.perimeter,
);
@ -135,7 +135,7 @@ impl SelectBoundary {
changed_blocks.push(id);
if app.session.partitioning.recalculate_coloring() {
// The coloring of neighborhoods changed; this could possibly have impact far
// The coloring of neighbourhoods changed; this could possibly have impact far
// away. Just redraw all blocks.
changed_blocks.clear();
changed_blocks.extend(app.session.partitioning.all_block_ids());
@ -161,16 +161,16 @@ impl SelectBoundary {
Transition::Keep
}
// Ok(Some(x)) means the current neighborhood was destroyed, and the caller should switch to
// Ok(Some(x)) means the current neighbourhood was destroyed, and the caller should switch to
// focusing on a different neigbhorhood
fn try_toggle_block(&mut self, app: &mut App, id: BlockID) -> Result<Option<NeighborhoodID>> {
fn try_toggle_block(&mut self, app: &mut App, id: BlockID) -> Result<Option<NeighbourhoodID>> {
if self.currently_have_block(app, id) {
app.session
.partitioning
.remove_block_from_neighborhood(&app.map, id, self.id)
.remove_block_from_neighbourhood(&app.map, id, self.id)
} else {
let old_owner = app.session.partitioning.block_to_neighborhood(id);
// Ignore the return value if the old neighborhood is deleted
let old_owner = app.session.partitioning.block_to_neighbourhood(id);
// Ignore the return value if the old neighbourhood is deleted
app.session
.partitioning
.transfer_block(&app.map, id, old_owner, self.id)?;
@ -179,17 +179,17 @@ impl SelectBoundary {
}
fn currently_have_block(&self, app: &App, id: BlockID) -> bool {
app.session.partitioning.block_to_neighborhood(id) == self.id
app.session.partitioning.block_to_neighbourhood(id) == self.id
}
fn add_blocks_freehand(&mut self, ctx: &mut EventCtx, app: &mut App, lasso_polygon: Polygon) {
ctx.loading_screen("expand current neighborhood boundary", |ctx, timer| {
ctx.loading_screen("expand current neighbourhood boundary", |ctx, timer| {
timer.start("find matching blocks");
// Find all of the blocks within the polygon
let mut add_blocks = Vec::new();
for (id, block) in app.session.partitioning.all_single_blocks() {
if lasso_polygon.contains_pt(block.polygon.center()) {
if app.session.partitioning.block_to_neighborhood(id) != self.id {
if app.session.partitioning.block_to_neighbourhood(id) != self.id {
add_blocks.push(id);
}
}
@ -208,7 +208,7 @@ impl SelectBoundary {
for block_id in add_blocks.drain(..) {
timer.next();
if self.frontier.contains(&block_id) {
let old_owner = app.session.partitioning.block_to_neighborhood(block_id);
let old_owner = app.session.partitioning.block_to_neighbourhood(block_id);
if let Ok(_) = app
.session
.partitioning
@ -227,7 +227,7 @@ impl SelectBoundary {
self.frontier = app.session.partitioning.calculate_frontier(
&app.session
.partitioning
.neighborhood_block(self.id)
.neighbourhood_block(self.id)
.perimeter,
);
} else {
@ -264,8 +264,8 @@ impl State<App> for SelectBoundary {
if let Outcome::Clicked(x) = self.left_panel.event(ctx) {
match x.as_ref() {
"Cancel" => {
// TODO If we destroyed the current neighborhood, then we cancel, we'll pop
// back to a different neighborhood than we started with. And also the original
// TODO If we destroyed the current neighbourhood, then we cancel, we'll pop
// back to a different neighbourhood than we started with. And also the original
// partitioning will have been lost!!!
app.session.partitioning = self.orig_partitioning.clone();
return Transition::Replace(crate::connectivity::Viewer::new_state(
@ -323,12 +323,12 @@ impl State<App> for SelectBoundary {
}
}
fn make_panel(ctx: &mut EventCtx, app: &App, id: NeighborhoodID, top_panel: &Panel) -> Panel {
fn make_panel(ctx: &mut EventCtx, app: &App, id: NeighbourhoodID, top_panel: &Panel) -> Panel {
crate::components::LeftPanel::builder(
ctx,
top_panel,
Widget::col(vec![
Line("Adjusting neighborhood boundary")
Line("Adjusting neighbourhood boundary")
.small_heading()
.into_widget(ctx),
Text::from_all(vec![
@ -349,8 +349,8 @@ fn make_panel(ctx: &mut EventCtx, app: &App, id: NeighborhoodID, top_panel: &Pan
])
.into_widget(ctx),
format!(
"Neighborhood area: {}",
app.session.partitioning.neighborhood_area_km2(id)
"Neighbourhood area: {}",
app.session.partitioning.neighbourhood_area_km2(id)
)
.text_widget(ctx),
ctx.style()
@ -381,12 +381,12 @@ fn make_panel_for_lasso(ctx: &mut EventCtx, top_panel: &Panel) -> Panel {
ctx,
top_panel,
Widget::col(vec![
"Draw a custom boundary for a neighborhood"
"Draw a custom boundary for a neighbourhood"
.text_widget(ctx)
.centered_vert(),
Text::from_all(vec![
Line("Click and drag").fg(ctx.style().text_hotkey_color),
Line(" to select the blocks to add to this neighborhood"),
Line(" to select the blocks to add to this neighbourhood"),
])
.into_widget(ctx),
]),
@ -396,7 +396,7 @@ fn make_panel_for_lasso(ctx: &mut EventCtx, top_panel: &Panel) -> Panel {
fn help() -> Vec<&'static str> {
vec![
"You can grow or shrink the blue neighborhood boundary here.",
"You can grow or shrink the blue neighbourhood boundary here.",
"Due to various known issues, it's not always possible to draw the boundary you want.",
"",
"The aqua blocks show where you can currently expand the boundary.",

View File

@ -3,9 +3,9 @@ use map_model::{PathRequest, NORMAL_LANE_THICKNESS};
use widgetry::mapspace::ToggleZoomed;
use widgetry::{EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, Text, TextExt, Widget};
use crate::edit::{EditNeighborhood, Tab};
use crate::edit::{EditNeighbourhood, Tab};
use crate::shortcuts::{find_shortcuts, Shortcuts};
use crate::{colors, App, Neighborhood, NeighborhoodID, Transition};
use crate::{colors, App, Neighbourhood, NeighbourhoodID, Transition};
pub struct BrowseShortcuts {
top_panel: Panel,
@ -14,23 +14,23 @@ pub struct BrowseShortcuts {
current_idx: usize,
draw_path: ToggleZoomed,
edit: EditNeighborhood,
neighborhood: Neighborhood,
edit: EditNeighbourhood,
neighbourhood: Neighbourhood,
}
impl BrowseShortcuts {
pub fn new_state(
ctx: &mut EventCtx,
app: &App,
id: NeighborhoodID,
id: NeighbourhoodID,
start_with_request: Option<PathRequest>,
) -> Box<dyn State<App>> {
let neighborhood = Neighborhood::new(ctx, app, id);
let neighbourhood = Neighbourhood::new(ctx, app, id);
let shortcuts = ctx.loading_screen("find shortcuts", |_, timer| {
find_shortcuts(app, &neighborhood, timer)
find_shortcuts(app, &neighbourhood, timer)
});
let edit = EditNeighborhood::new(ctx, app, &neighborhood, &shortcuts);
let edit = EditNeighbourhood::new(ctx, app, &neighbourhood, &shortcuts);
let mut state = BrowseShortcuts {
top_panel: crate::components::TopPanel::panel(ctx, app),
@ -38,7 +38,7 @@ impl BrowseShortcuts {
shortcuts,
current_idx: 0,
draw_path: ToggleZoomed::empty(ctx),
neighborhood,
neighbourhood,
edit,
};
@ -60,7 +60,7 @@ impl BrowseShortcuts {
fn recalculate(&mut self, ctx: &mut EventCtx, app: &App) {
let (quiet_streets, total_streets) =
self.shortcuts.quiet_and_total_streets(&self.neighborhood);
self.shortcuts.quiet_and_total_streets(&self.neighbourhood);
if self.shortcuts.paths.is_empty() {
self.left_panel = self
@ -104,7 +104,7 @@ impl BrowseShortcuts {
))),
(quiet_streets as f64) / (total_streets as f64),
),
"Browse possible shortcuts through the neighborhood.".text_widget(ctx),
"Browse possible shortcuts through the neighbourhood.".text_widget(ctx),
self.prev_next_controls(ctx),
]),
)
@ -182,7 +182,7 @@ impl State<App> for BrowseShortcuts {
ctx,
app,
x,
&self.neighborhood,
&self.neighbourhood,
&self.left_panel,
) {
return t;
@ -199,7 +199,7 @@ impl State<App> for BrowseShortcuts {
current_request,
app.session
.partitioning
.all_blocks_in_neighborhood(self.neighborhood.id),
.all_blocks_in_neighbourhood(self.neighbourhood.id),
),
x,
)
@ -215,7 +215,7 @@ impl State<App> for BrowseShortcuts {
return Transition::Replace(BrowseShortcuts::new_state(
ctx,
app,
self.neighborhood.id,
self.neighbourhood.id,
Some(current_request),
));
}
@ -230,10 +230,10 @@ impl State<App> for BrowseShortcuts {
self.edit.world.draw(g);
self.draw_path.draw(g);
g.redraw(&self.neighborhood.fade_irrelevant);
g.redraw(&self.neighbourhood.fade_irrelevant);
app.session.draw_all_filters.draw(g);
if g.canvas.is_unzoomed() {
self.neighborhood.labels.draw(g, app);
self.neighbourhood.labels.draw(g, app);
}
}
@ -243,13 +243,13 @@ impl State<App> for BrowseShortcuts {
} else {
Some(self.shortcuts.paths[self.current_idx].get_req().clone())
};
Self::new_state(ctx, app, self.neighborhood.id, current_request)
Self::new_state(ctx, app, self.neighbourhood.id, current_request)
}
}
fn help() -> Vec<&'static str> {
vec![
"This shows every possible path a driver could take through the neighborhood.",
"This shows every possible path a driver could take through the neighbourhood.",
"Not all paths may be realistic -- small service roads and alleyways are possible, but unlikely.",
]
}

View File

@ -6,7 +6,7 @@ use map_model::{
Pathfinder, Position, RoadID,
};
use crate::{App, Cell, Neighborhood};
use crate::{App, Cell, Neighbourhood};
pub struct Shortcuts {
pub paths: Vec<Path>,
@ -15,19 +15,19 @@ pub struct Shortcuts {
}
impl Shortcuts {
pub fn quiet_and_total_streets(&self, neighborhood: &Neighborhood) -> (usize, usize) {
let quiet_streets = neighborhood
pub fn quiet_and_total_streets(&self, neighbourhood: &Neighbourhood) -> (usize, usize) {
let quiet_streets = neighbourhood
.orig_perimeter
.interior
.iter()
.filter(|r| self.count_per_road.get(**r) == 0)
.count();
let total_streets = neighborhood.orig_perimeter.interior.len();
let total_streets = neighbourhood.orig_perimeter.interior.len();
(quiet_streets, total_streets)
}
}
pub fn find_shortcuts(app: &App, neighborhood: &Neighborhood, timer: &mut Timer) -> Shortcuts {
pub fn find_shortcuts(app: &App, neighbourhood: &Neighbourhood, timer: &mut Timer) -> Shortcuts {
let map = &app.map;
let modal_filters = &app.session.modal_filters;
// The overall approach: look for all possible paths from an entrance to an exit, only if they
@ -39,9 +39,9 @@ pub fn find_shortcuts(app: &App, neighborhood: &Neighborhood, timer: &mut Timer)
// to pairs of entrances/exits in the _same_ cell.
let mut requests = Vec::new();
for cell in &neighborhood.cells {
let entrances = find_entrances(map, neighborhood, cell);
let exits = find_exits(map, neighborhood, cell);
for cell in &neighbourhood.cells {
let entrances = find_entrances(map, neighbourhood, cell);
let exits = find_exits(map, neighbourhood, cell);
for entrance in &entrances {
for exit in &exits {
@ -58,12 +58,12 @@ pub fn find_shortcuts(app: &App, neighborhood: &Neighborhood, timer: &mut Timer)
let mut params = map.routing_params().clone();
modal_filters.update_routing_params(&mut params);
// Don't allow leaving the neighborhood and using perimeter roads at all. Even if the optimal
// Don't allow leaving the neighbourhood and using perimeter roads at all. Even if the optimal
// path is to leave and re-enter, don't do that. The point of this view is to show possible
// detours people might try to take in response to one filter. Note the original "demand model"
// input is bogus anyway; it's all possible entrances and exits to the neighborhood, without
// input is bogus anyway; it's all possible entrances and exits to the neighbourhood, without
// regards for the larger path somebody actually wants to take.
params.avoid_roads.extend(neighborhood.perimeter.clone());
params.avoid_roads.extend(neighbourhood.perimeter.clone());
let pathfinder = Pathfinder::new_dijkstra(map, params, vec![PathConstraints::Car], timer);
let paths: Vec<Path> = timer
@ -93,12 +93,12 @@ pub fn find_shortcuts(app: &App, neighborhood: &Neighborhood, timer: &mut Timer)
for step in path.get_steps() {
match step {
PathStep::Lane(l) => {
if neighborhood.orig_perimeter.interior.contains(&l.road) {
if neighbourhood.orig_perimeter.interior.contains(&l.road) {
count_per_road.inc(l.road);
}
}
PathStep::Turn(t) => {
if neighborhood.interior_intersections.contains(&t.parent) {
if neighbourhood.interior_intersections.contains(&t.parent) {
count_per_intersection.inc(t.parent);
}
}
@ -122,10 +122,10 @@ struct EntryExit {
major_road_name: String,
}
fn find_entrances(map: &Map, neighborhood: &Neighborhood, cell: &Cell) -> Vec<EntryExit> {
fn find_entrances(map: &Map, neighbourhood: &Neighbourhood, cell: &Cell) -> Vec<EntryExit> {
let mut entrances = Vec::new();
for i in &cell.borders {
if let Some(major_road_name) = find_major_road_name(map, neighborhood, *i) {
if let Some(major_road_name) = find_major_road_name(map, neighbourhood, *i) {
let mut seen: HashSet<DirectedRoadID> = HashSet::new();
for l in map.get_i(*i).get_outgoing_lanes(map, PathConstraints::Car) {
let dr = map.get_l(l).get_directed_parent();
@ -142,10 +142,10 @@ fn find_entrances(map: &Map, neighborhood: &Neighborhood, cell: &Cell) -> Vec<En
entrances
}
fn find_exits(map: &Map, neighborhood: &Neighborhood, cell: &Cell) -> Vec<EntryExit> {
fn find_exits(map: &Map, neighbourhood: &Neighbourhood, cell: &Cell) -> Vec<EntryExit> {
let mut exits = Vec::new();
for i in &cell.borders {
if let Some(major_road_name) = find_major_road_name(map, neighborhood, *i) {
if let Some(major_road_name) = find_major_road_name(map, neighbourhood, *i) {
let mut seen: HashSet<DirectedRoadID> = HashSet::new();
for l in map.get_i(*i).get_incoming_lanes(map, PathConstraints::Car) {
let dr = map.get_l(l).get_directed_parent();
@ -164,12 +164,12 @@ fn find_exits(map: &Map, neighborhood: &Neighborhood, cell: &Cell) -> Vec<EntryE
fn find_major_road_name(
map: &Map,
neighborhood: &Neighborhood,
neighbourhood: &Neighbourhood,
i: IntersectionID,
) -> Option<String> {
let mut names = Vec::new();
for r in &map.get_i(i).roads {
if neighborhood.perimeter.contains(r) {
if neighbourhood.perimeter.contains(r) {
names.push(map.get_r(*r).get_name(None));
}
}