Change language of roads via settings, for #271

This commit is contained in:
Dustin Carlino 2020-08-18 17:32:49 -07:00
parent 2e93fdbf54
commit 6749edb2b6
19 changed files with 117 additions and 40 deletions

View File

@ -133,7 +133,7 @@ impl CommonState {
let r = map.get_parent(l); let r = map.get_parent(l);
osd.append_all(vec![ osd.append_all(vec![
Line(format!("{} of ", map.get_l(l).lane_type.describe())), Line(format!("{} of ", map.get_l(l).lane_type.describe())),
Line(r.get_name()).fg(name_color), Line(r.get_name(app.opts.language.as_ref())).fg(name_color),
]); ]);
if app.opts.dev { if app.opts.dev {
osd.append(Line(" (")); osd.append(Line(" ("));
@ -166,7 +166,7 @@ impl CommonState {
let mut road_names = BTreeSet::new(); let mut road_names = BTreeSet::new();
for r in &map.get_i(i).roads { for r in &map.get_i(i).roads {
road_names.insert(map.get_r(*r).get_name()); road_names.insert(map.get_r(*r).get_name(app.opts.language.as_ref()));
} }
list_names(&mut osd, |l| l.fg(name_color), road_names); list_names(&mut osd, |l| l.fg(name_color), road_names);
} }
@ -218,7 +218,7 @@ impl CommonState {
osd.append(Line(r.to_string()).fg(id_color)); osd.append(Line(r.to_string()).fg(id_color));
osd.append(Line(" is ")); osd.append(Line(" is "));
} }
osd.append(Line(map.get_r(r).get_name()).fg(name_color)); osd.append(Line(map.get_r(r).get_name(app.opts.language.as_ref())).fg(name_color));
} }
} }
osd osd

View File

@ -30,7 +30,7 @@ impl Navigator {
.map .map
.all_roads() .all_roads()
.iter() .iter()
.map(|r| (r.get_name(), r.id)) .map(|r| (r.get_name(app.opts.language.as_ref()), r.id))
.collect(), .collect(),
) )
.named("street"), .named("street"),
@ -108,7 +108,7 @@ impl CrossStreet {
// TODO This isn't so clear... // TODO This isn't so clear...
txt.add(Line(format!( txt.add(Line(format!(
"(Or just quit to go to {})", "(Or just quit to go to {})",
map.get_r(first[0]).get_name(), map.get_r(first[0]).get_name(app.opts.language.as_ref()),
))); )));
txt.draw(ctx) txt.draw(ctx)
}, },
@ -120,7 +120,7 @@ impl CrossStreet {
ctx, ctx,
cross_streets cross_streets
.into_iter() .into_iter()
.map(|r| (map.get_r(r).get_name(), r)) .map(|r| (map.get_r(r).get_name(app.opts.language.as_ref()), r))
.collect(), .collect(),
) )
.named("street"), .named("street"),

View File

@ -640,7 +640,8 @@ fn find_divided_highways(app: &App) -> HashSet<RoadID> {
]) ])
.intersection(&map.get_r(r2).center_pts) .intersection(&map.get_r(r2).center_pts)
.is_some() .is_some()
&& r1.get_name() == map.get_r(r2).get_name() && r1.get_name(app.opts.language.as_ref())
== map.get_r(r2).get_name(app.opts.language.as_ref())
{ {
found.insert(r1.id); found.insert(r1.id);
found.insert(r2); found.insert(r2);

View File

@ -75,9 +75,12 @@ impl LaneEditor {
let parent = app.primary.map.get_parent(l); let parent = app.primary.map.get_parent(l);
let col = vec![ let col = vec![
format!("Convert this lane of {} to what type?", parent.get_name()) format!(
.draw_text(ctx) "Convert this lane of {} to what type?",
.centered_horiz(), parent.get_name(app.opts.language.as_ref())
)
.draw_text(ctx)
.centered_horiz(),
Widget::custom_row(row).centered(), Widget::custom_row(row).centered(),
change_speed_limit(ctx, parent.speed_limit), change_speed_limit(ctx, parent.speed_limit),
Btn::text_fg("Change access restrictions").build_def(ctx, hotkey(Key::A)), Btn::text_fg("Change access restrictions").build_def(ctx, hotkey(Key::A)),

View File

@ -210,7 +210,13 @@ impl State for StopSignEditor {
let mut osd = Text::new(); let mut osd = Text::new();
osd.add_appended(vec![ osd.add_appended(vec![
Line("Stop sign for "), Line("Stop sign for "),
Line(app.primary.map.get_r(r).get_name()).fg(app.cs.bottom_bar_name), Line(
app.primary
.map
.get_r(r)
.get_name(app.opts.language.as_ref()),
)
.fg(app.cs.bottom_bar_name),
]); ]);
CommonState::draw_custom_osd(g, app, osd); CommonState::draw_custom_osd(g, app, osd);
} else { } else {

View File

@ -478,13 +478,22 @@ impl State for TrafficSignalEditor {
let osd = if id.crosswalk { let osd = if id.crosswalk {
Text::from(Line(format!( Text::from(Line(format!(
"Crosswalk across {}", "Crosswalk across {}",
app.primary.map.get_r(id.from.id).get_name() app.primary
.map
.get_r(id.from.id)
.get_name(app.opts.language.as_ref())
))) )))
} else { } else {
Text::from(Line(format!( Text::from(Line(format!(
"Turn from {} to {}", "Turn from {} to {}",
app.primary.map.get_r(id.from.id).get_name(), app.primary
app.primary.map.get_r(id.to.id).get_name() .map
.get_r(id.from.id)
.get_name(app.opts.language.as_ref()),
app.primary
.map
.get_r(id.to.id)
.get_name(app.opts.language.as_ref())
))) )))
}; };
CommonState::draw_custom_osd(g, app, osd); CommonState::draw_custom_osd(g, app, osd);
@ -564,7 +573,12 @@ fn make_side_panel(
let mut road_names = BTreeSet::new(); let mut road_names = BTreeSet::new();
for r in &app.primary.map.get_i(i).roads { for r in &app.primary.map.get_i(i).roads {
road_names.insert(app.primary.map.get_r(*r).get_name()); road_names.insert(
app.primary
.map
.get_r(*r)
.get_name(app.opts.language.as_ref()),
);
} }
for r in road_names { for r in road_names {
txt.add(Line(format!("- {}", r))); txt.add(Line(format!("- {}", r)));

View File

@ -237,7 +237,7 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
rows.push(format!("{} stops", route.stops.len()).draw_text(ctx)); rows.push(format!("{} stops", route.stops.len()).draw_text(ctx));
{ {
let i = map.get_i(map.get_l(route.start).src_i); let i = map.get_i(map.get_l(route.start).src_i);
let name = format!("Starts at {}", i.name(map)); let name = format!("Starts at {}", i.name(app.opts.language.as_ref(), map));
rows.push(Widget::row(vec![ rows.push(Widget::row(vec![
Btn::svg( Btn::svg(
"system/assets/timeline/goal_pos.svg", "system/assets/timeline/goal_pos.svg",
@ -273,7 +273,7 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
} }
if let Some(l) = route.end_border { if let Some(l) = route.end_border {
let i = map.get_i(map.get_l(l).dst_i); let i = map.get_i(map.get_l(l).dst_i);
let name = format!("Ends at {}", i.name(map)); let name = format!("Ends at {}", i.name(app.opts.language.as_ref(), map));
rows.push(Widget::row(vec![ rows.push(Widget::row(vec![
Btn::svg( Btn::svg(
"system/assets/timeline/goal_pos.svg", "system/assets/timeline/goal_pos.svg",

View File

@ -20,7 +20,12 @@ pub fn info(ctx: &EventCtx, app: &App, details: &mut Details, id: IntersectionID
let mut txt = Text::from(Line("Connecting")); let mut txt = Text::from(Line("Connecting"));
let mut road_names = BTreeSet::new(); let mut road_names = BTreeSet::new();
for r in &i.roads { for r in &i.roads {
road_names.insert(app.primary.map.get_r(*r).get_name()); road_names.insert(
app.primary
.map
.get_r(*r)
.get_name(app.opts.language.as_ref()),
);
} }
for r in road_names { for r in road_names {
// TODO The spacing is ignored, so use - // TODO The spacing is ignored, so use -

View File

@ -222,7 +222,7 @@ fn header(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID, tab: Tab
.draw(ctx), .draw(ctx),
header_btns(ctx), header_btns(ctx),
])); ]));
rows.push(format!("@ {}", r.get_name()).draw_text(ctx)); rows.push(format!("@ {}", r.get_name(app.opts.language.as_ref())).draw_text(ctx));
let mut tabs = vec![("Info", Tab::LaneInfo(id))]; let mut tabs = vec![("Info", Tab::LaneInfo(id))];
if !l.is_parking() { if !l.is_parking() {

View File

@ -160,8 +160,8 @@ pub fn future(
)); ));
} else { } else {
// TODO Warp buttons. make_table is showing its age. // TODO Warp buttons. make_table is showing its age.
let (id1, _, name1) = endpoint(&trip.start, &app.primary.map); let (id1, _, name1) = endpoint(&trip.start, app);
let (id2, _, name2) = endpoint(&trip.end, &app.primary.map); let (id2, _, name2) = endpoint(&trip.end, app);
details details
.warpers .warpers
.insert(format!("jump to start of {}", id), id1); .insert(format!("jump to start of {}", id), id1);
@ -323,8 +323,8 @@ pub fn aborted(ctx: &mut EventCtx, app: &App, id: TripID) -> Widget {
.draw(ctx)]; .draw(ctx)];
// TODO Warp buttons. make_table is showing its age. // TODO Warp buttons. make_table is showing its age.
let (_, _, name1) = endpoint(&trip.start, &app.primary.map); let (_, _, name1) = endpoint(&trip.start, app);
let (_, _, name2) = endpoint(&trip.end, &app.primary.map); let (_, _, name2) = endpoint(&trip.end, app);
col.extend(make_table( col.extend(make_table(
ctx, ctx,
vec![ vec![
@ -344,8 +344,8 @@ pub fn cancelled(ctx: &mut EventCtx, app: &App, id: TripID) -> Widget {
let mut col = vec!["Trip cancelled due to traffic pattern modifications".draw_text(ctx)]; let mut col = vec!["Trip cancelled due to traffic pattern modifications".draw_text(ctx)];
// TODO Warp buttons. make_table is showing its age. // TODO Warp buttons. make_table is showing its age.
let (_, _, name1) = endpoint(&trip.start, &app.primary.map); let (_, _, name1) = endpoint(&trip.start, app);
let (_, _, name2) = endpoint(&trip.end, &app.primary.map); let (_, _, name2) = endpoint(&trip.end, app);
col.extend(make_table( col.extend(make_table(
ctx, ctx,
vec![ vec![
@ -374,7 +374,7 @@ fn make_timeline(
let end_time = phases.last().as_ref().and_then(|p| p.end_time); let end_time = phases.last().as_ref().and_then(|p| p.end_time);
let start_btn = { let start_btn = {
let (id, center, name) = endpoint(&trip.start, map); let (id, center, name) = endpoint(&trip.start, app);
details details
.warpers .warpers
.insert(format!("jump to start of {}", trip_id), id); .insert(format!("jump to start of {}", trip_id), id);
@ -419,7 +419,7 @@ fn make_timeline(
}; };
let goal_btn = { let goal_btn = {
let (id, center, name) = endpoint(&trip.end, map); let (id, center, name) = endpoint(&trip.end, app);
details details
.warpers .warpers
.insert(format!("jump to goal of {}", trip_id), id); .insert(format!("jump to goal of {}", trip_id), id);
@ -699,18 +699,21 @@ fn make_elevation(ctx: &EventCtx, color: Color, walking: bool, path: &Path, map:
} }
// (ID, center, name) // (ID, center, name)
fn endpoint(endpt: &TripEndpoint, map: &Map) -> (ID, Pt2D, String) { fn endpoint(endpt: &TripEndpoint, app: &App) -> (ID, Pt2D, String) {
match endpt { match endpt {
TripEndpoint::Bldg(b) => { TripEndpoint::Bldg(b) => {
let bldg = map.get_b(*b); let bldg = app.primary.map.get_b(*b);
(ID::Building(*b), bldg.label_center, bldg.address.clone()) (ID::Building(*b), bldg.label_center, bldg.address.clone())
} }
TripEndpoint::Border(i, _) => { TripEndpoint::Border(i, _) => {
let i = map.get_i(*i); let i = app.primary.map.get_i(*i);
( (
ID::Intersection(i.id), ID::Intersection(i.id),
i.polygon.center(), i.polygon.center(),
format!("off map, via {}", i.name(map)), format!(
"off map, via {}",
i.name(app.opts.language.as_ref(), &app.primary.map)
),
) )
} }
} }

View File

@ -22,6 +22,8 @@ pub struct Options {
pub time_increment: Duration, pub time_increment: Duration,
pub resume_after_edit: bool, pub resume_after_edit: bool,
pub dont_draw_time_warp: bool, pub dont_draw_time_warp: bool,
pub language: Option<String>,
} }
impl Options { impl Options {
@ -39,6 +41,8 @@ impl Options {
time_increment: Duration::minutes(10), time_increment: Duration::minutes(10),
resume_after_edit: true, resume_after_edit: true,
dont_draw_time_warp: false, dont_draw_time_warp: false,
language: None,
} }
} }
} }
@ -167,6 +171,17 @@ impl OptionsPanel {
None, None,
app.opts.large_unzoomed_agents, app.opts.large_unzoomed_agents,
), ),
Widget::row(vec![
"Language".draw_text(ctx),
Widget::dropdown(ctx, "language", app.opts.language.clone(), {
let mut choices = Vec::new();
choices.push(Choice::new("Map native language", None));
for lang in app.primary.map.get_languages() {
choices.push(Choice::new(lang, Some(lang.to_string())));
}
choices
}),
]),
]) ])
.bg(app.cs.section_bg) .bg(app.cs.section_bg)
.padding(8), .padding(8),
@ -222,7 +237,7 @@ impl State for OptionsPanel {
if app.opts.traffic_signal_style != style { if app.opts.traffic_signal_style != style {
app.opts.traffic_signal_style = style; app.opts.traffic_signal_style = style;
println!("Rerendering traffic signals..."); println!("Rerendering traffic signals...");
for i in app.primary.draw_map.intersections.iter_mut() { for i in &mut app.primary.draw_map.intersections {
*i.draw_traffic_signal.borrow_mut() = None; *i.draw_traffic_signal.borrow_mut() = None;
} }
} }
@ -237,6 +252,14 @@ impl State for OptionsPanel {
app.opts.large_unzoomed_agents = app.opts.large_unzoomed_agents =
self.composite.is_checked("Draw enlarged unzoomed agents"); self.composite.is_checked("Draw enlarged unzoomed agents");
let language = self.composite.dropdown_value("language");
if language != app.opts.language {
app.opts.language = language;
for r in &mut app.primary.draw_map.roads {
r.clear_rendering();
}
}
return Transition::Pop; return Transition::Pop;
} }
_ => unreachable!(), _ => unreachable!(),

View File

@ -26,6 +26,7 @@ impl DrawRoad {
pub fn clear_rendering(&mut self) { pub fn clear_rendering(&mut self) {
*self.draw_center_line.borrow_mut() = None; *self.draw_center_line.borrow_mut() = None;
*self.label.borrow_mut() = None;
} }
} }
@ -73,7 +74,7 @@ impl Renderable for DrawRoad {
let mut batch = GeomBatch::new(); let mut batch = GeomBatch::new();
let r = app.primary.map.get_r(self.id); let r = app.primary.map.get_r(self.id);
if !r.is_light_rail() { if !r.is_light_rail() {
let name = r.get_name(); let name = r.get_name(app.opts.language.as_ref());
if r.center_pts.length() >= Distance::meters(30.0) && name != "???" { if r.center_pts.length() >= Distance::meters(30.0) && name != "???" {
// TODO If it's definitely straddling bus/bike lanes, change the color? Or // TODO If it's definitely straddling bus/bike lanes, change the color? Or
// even easier, just skip the center lines? // even easier, just skip the center lines?

View File

@ -163,7 +163,10 @@ impl TurnExplorer {
Text::from( Text::from(
Line(format!( Line(format!(
"Turns from {}", "Turns from {}",
app.primary.map.get_parent(l).get_name() app.primary
.map
.get_parent(l)
.get_name(app.opts.language.as_ref())
)) ))
.small_heading(), .small_heading(),
) )

View File

@ -105,7 +105,7 @@ fn get_address(tags: &Tags, sidewalk: LaneID, map: &Map) -> String {
match (tags.get("addr:housenumber"), tags.get("addr:street")) { match (tags.get("addr:housenumber"), tags.get("addr:street")) {
(Some(num), Some(st)) => format!("{} {}", num, st), (Some(num), Some(st)) => format!("{} {}", num, st),
(None, Some(st)) => format!("??? {}", st), (None, Some(st)) => format!("??? {}", st),
_ => format!("??? {}", map.get_parent(sidewalk).get_name()), _ => format!("??? {}", map.get_parent(sidewalk).get_name(None)),
} }
} }

View File

@ -197,7 +197,7 @@ impl Map {
biking_blackhole: false, biking_blackhole: false,
}); });
} }
if road.get_name() == "???" { if road.get_name(None) == "???" {
// Suppress the warning in some cases. // Suppress the warning in some cases.
if !(road.osm_tags.is("noname", "yes") if !(road.osm_tags.is("noname", "yes")
|| road || road

View File

@ -26,7 +26,7 @@ pub fn get_possible_policies(
timer.error(format!( timer.error(format!(
"seattle_traffic_signals data for {} ({}) out of date, go update it", "seattle_traffic_signals data for {} ({}) out of date, go update it",
i.orig_id, i.orig_id,
i.name(map) i.name(None, map)
)); ));
} }
} }

View File

@ -633,4 +633,16 @@ impl Map {
self.bus_routes[br.0].orig_spawn_times = times.clone(); self.bus_routes[br.0].orig_spawn_times = times.clone();
self.bus_routes[br.0].spawn_times = times; self.bus_routes[br.0].spawn_times = times;
} }
pub fn get_languages(&self) -> BTreeSet<&str> {
let mut languages = BTreeSet::new();
for r in self.all_roads() {
for key in r.osm_tags.inner().keys() {
if let Some(x) = key.strip_prefix("name:") {
languages.insert(x);
}
}
}
languages
}
} }

View File

@ -142,11 +142,11 @@ impl Intersection {
.map(|l| map.get_l(*l).get_directed_parent(map)) .map(|l| map.get_l(*l).get_directed_parent(map))
} }
pub fn name(&self, map: &Map) -> String { pub fn name(&self, lang: Option<&String>, map: &Map) -> String {
let road_names = self let road_names = self
.roads .roads
.iter() .iter()
.map(|r| map.get_r(*r).get_name()) .map(|r| map.get_r(*r).get_name(lang))
.collect::<BTreeSet<_>>(); .collect::<BTreeSet<_>>();
abstutil::plain_list_names(road_names) abstutil::plain_list_names(road_names)
} }

View File

@ -293,7 +293,13 @@ impl Road {
.make_polygons(self.get_half_width(map) * 2.0) .make_polygons(self.get_half_width(map) * 2.0)
} }
pub fn get_name(&self) -> String { pub fn get_name(&self, lang: Option<&String>) -> String {
if let Some(lang) = lang {
if let Some(name) = self.osm_tags.get(&format!("name:{}", lang)) {
return name.to_string();
}
}
if let Some(name) = self.osm_tags.get(osm::NAME) { if let Some(name) = self.osm_tags.get(osm::NAME) {
if name == "" { if name == "" {
return "???".to_string(); return "???".to_string();