Include my own Seattle bike network vision as a built-in proposal. #743

To avoid needing 3 copies of the proposal for different splits of the
map, make loading edits "permissive" (filtering out unknown roads) when
loading from proposals.
This commit is contained in:
Dustin Carlino 2021-10-11 17:34:14 -07:00
parent 5e592447b7
commit 5f83f5b46b
6 changed files with 60982 additions and 9 deletions

File diff suppressed because it is too large Load Diff

View File

@ -573,10 +573,17 @@ impl LoadEdits {
// Up-front filter out proposals that definitely don't fit the current map
for name in abstio::list_all_objects(abstio::path("system/proposals")) {
let path = abstio::path(format!("system/proposals/{}.json", name));
if MapEdits::load_from_file(&app.primary.map, path.clone(), &mut Timer::throwaway())
.is_ok()
{
proposals.push(ctx.style().btn_outline.text(name).build_widget(ctx, &path));
if let Ok(edits) = MapEdits::load_from_file_permissive(
&app.primary.map,
path.clone(),
&mut Timer::throwaway(),
) {
proposals.push(
ctx.style()
.btn_outline
.text(edits.get_title())
.build_widget(ctx, &path),
);
}
}
@ -618,7 +625,7 @@ impl State<App> for LoadEdits {
abstio::path_edits(app.primary.map.get_name(), path)
};
match MapEdits::load_from_file(
match MapEdits::load_from_file_permissive(
&app.primary.map,
path.clone(),
&mut Timer::throwaway(),

View File

@ -357,7 +357,8 @@ fn continue_app_setup(
abstio::path(format!("system/proposals/{}.json", edits_name)),
] {
if abstio::file_exists(&path) {
let edits = map_model::MapEdits::load_from_file(
// TODO Technically we should only be permissive if this was a proposal
let edits = map_model::MapEdits::load_from_file_permissive(
&app.primary.map,
path,
&mut Timer::throwaway(),

View File

@ -33,7 +33,7 @@ impl Proposals {
{
if current == Some(name.clone()) {
let mut txt = Text::new();
txt.add_line(Line(&edits.proposal_description[0]).small_heading());
txt.add_line(Line(edits.get_title()).small_heading());
for l in edits.proposal_description.iter().skip(1) {
txt.add_line(l);
}
@ -65,7 +65,7 @@ impl Proposals {
tab_buttons.push(
ctx.style()
.btn_tab
.text(&edits.proposal_description[0])
.text(edits.get_title())
.disabled(true)
.build_def(ctx)
.margin_below(10),
@ -77,7 +77,7 @@ impl Proposals {
tab_buttons.push(
ctx.style()
.btn_outline
.text(&edits.proposal_description[0])
.text(edits.get_title())
.no_tooltip()
.hotkey(hotkey)
.build_widget(ctx, &name)

View File

@ -217,6 +217,7 @@ impl MapEdits {
}
}
/// Load map edits from a JSON file. If any part of the edits don't match the current map, fail.
pub fn load_from_file(map: &Map, path: String, timer: &mut Timer) -> Result<MapEdits> {
match abstio::maybe_read_json::<PermanentMapEdits>(path.clone(), timer) {
Ok(perma) => perma.into_edits(map),
@ -231,6 +232,8 @@ impl MapEdits {
}
}
/// Load map edits from the given JSON bytes. If any part of the edits don't match the current
/// map, fail.
pub fn load_from_bytes(map: &Map, bytes: Vec<u8>) -> Result<MapEdits> {
match abstutil::from_json::<PermanentMapEdits>(&bytes) {
Ok(perma) => perma.into_edits(map),
@ -244,6 +247,32 @@ impl MapEdits {
}
}
/// Load map edits from a JSON file. Strip out any commands that're broken because they don't
/// match the current map. If the resulting edits are totally empty, consider that a failure --
/// the edits likely don't cover this map at all. This is currently most appropriate when using
/// system proposals that cover a large area.
pub fn load_from_file_permissive(
map: &Map,
path: String,
timer: &mut Timer,
) -> Result<MapEdits> {
let perma = match abstio::maybe_read_json::<PermanentMapEdits>(path.clone(), timer) {
Ok(perma) => perma,
Err(_) => {
// The JSON format may have changed, so attempt backwards compatibility.
let bytes = abstio::slurp_file(path)?;
let contents = std::str::from_utf8(&bytes)?;
let value = serde_json::from_str(contents)?;
compat::upgrade(value, map)?
}
};
let edits = perma.into_edits_permissive(map);
if edits.commands.is_empty() {
bail!("None of the edits apply to this map");
}
Ok(edits)
}
fn save(&self, map: &Map) {
// If untitled and empty, don't actually save anything.
if self.edits_name.starts_with("Untitled Proposal") && self.commands.is_empty() {
@ -348,6 +377,16 @@ impl MapEdits {
context.consume(&bytes);
format!("{:x}", context.compute())
}
/// Get the human-friendly of these edits. If they have a descrption, the first line is the
/// title. Otherwise we use the filename.
pub fn get_title(&self) -> &str {
if self.proposal_description.is_empty() {
&self.edits_name
} else {
&self.proposal_description[0]
}
}
}
impl Default for MapEdits {

View File

@ -192,6 +192,16 @@ impl PermanentMapEdits {
edits.update_derived(map);
edits
}
/// Get the human-friendly of these edits. If they have a descrption, the first line is the
/// title. Otherwise we use the filename.
pub fn get_title(&self) -> &str {
if self.proposal_description.is_empty() {
&self.edits_name
} else {
&self.proposal_description[0]
}
}
}
impl EditIntersection {