diff --git a/data/MANIFEST.json b/data/MANIFEST.json index d8f76273a4..7bd0248f56 100644 --- a/data/MANIFEST.json +++ b/data/MANIFEST.json @@ -1441,9 +1441,9 @@ "compressed_size_bytes": 3896689 }, "data/input/gb/london/screenshots/kennington.zip": { - "checksum": "1f023c54a925f4d54afb2a9a3a74ecea", - "uncompressed_size_bytes": 6674339, - "compressed_size_bytes": 6673167 + "checksum": "19abb1d8c10ca063901ab9c876e5eca6", + "uncompressed_size_bytes": 6600386, + "compressed_size_bytes": 6599250 }, "data/input/gb/long_marston/osm/center.osm": { "checksum": "c7c25ca197870b843ac79c591c1275f3", @@ -2091,9 +2091,9 @@ "compressed_size_bytes": 3215418 }, "data/input/pl/krakow/screenshots/center.zip": { - "checksum": "ff5f39a48d7f6ea1943e2abd5569937f", - "uncompressed_size_bytes": 37103479, - "compressed_size_bytes": 37099988 + "checksum": "c5927c5639d035c02f9f075fdcc2fa54", + "uncompressed_size_bytes": 36713667, + "compressed_size_bytes": 36710148 }, "data/input/pl/warsaw/osm/center.osm": { "checksum": "b41830dd375674ffc9f7ec15d6cf9c0c", @@ -2731,9 +2731,9 @@ "compressed_size_bytes": 384258 }, "data/input/us/phoenix/screenshots/tempe.zip": { - "checksum": "32665a738ab6e43098feae621a4873b8", - "uncompressed_size_bytes": 10152381, - "compressed_size_bytes": 10150625 + "checksum": "cbf1aae43c64bb83a630a7ea16e54127", + "uncompressed_size_bytes": 10088176, + "compressed_size_bytes": 10086412 }, "data/input/us/providence/osm/downtown.osm": { "checksum": "463b986adc83ae4d1174496a4ce744d1", @@ -3081,14 +3081,14 @@ "compressed_size_bytes": 4992247 }, "data/input/us/seattle/screenshots/downtown.zip": { - "checksum": "1713164bc2544478f58a9e0dcc799481", - "uncompressed_size_bytes": 28777110, - "compressed_size_bytes": 28769092 + "checksum": "45bd51e7ac9c59e73ff65acbd0dfcf18", + "uncompressed_size_bytes": 28343657, + "compressed_size_bytes": 28336193 }, "data/input/us/seattle/screenshots/montlake.zip": { - "checksum": "486a02377c86064a584c60cc8939c47f", - "uncompressed_size_bytes": 5455080, - "compressed_size_bytes": 5455031 + "checksum": "d0c79530e0ad648bf83f13549448015e", + "uncompressed_size_bytes": 5402505, + "compressed_size_bytes": 5402436 }, "data/input/us/seattle/trips_2014.csv": { "checksum": "d4a8e733045b28c0385fb81359d6df03", diff --git a/map_gui/src/render/road.rs b/map_gui/src/render/road.rs index fc4e5f4810..244066bfea 100644 --- a/map_gui/src/render/road.rs +++ b/map_gui/src/render/road.rs @@ -10,6 +10,12 @@ use crate::render::lane::DrawLane; use crate::render::{DrawOptions, Renderable}; use crate::{AppLike, ID}; +// The default font size is too large; shrink it down to fit on roads better. +const LABEL_SCALE_FACTOR: f64 = 0.1; +// Making the label follow the road's curvature usually looks better, but sometimes the letters +// squish together, so keep this experiment disabled for now. +const DRAW_CURVEY_LABEL: bool = false; + pub struct DrawRoad { pub id: RoadID, zorder: isize, @@ -28,95 +34,57 @@ impl DrawRoad { } } - pub fn render_center_line(&self, app: &dyn AppLike) -> GeomBatch { - let r = app.map().get_r(self.id); - let center_line_color = if r.is_private() && app.cs().private_road.is_some() { - app.cs() - .road_center_line - .lerp(app.cs().private_road.unwrap(), 0.5) - } else { - app.cs().road_center_line - }; - - let mut batch = GeomBatch::new(); - - // Draw a center line every time two driving/bike/bus lanes of opposite direction are - // adjacent. - let mut width = Distance::ZERO; - for pair in r.lanes.windows(2) { - width += pair[0].width; - if pair[0].dir != pair[1].dir - && pair[0].lane_type.is_for_moving_vehicles() - && pair[1].lane_type.is_for_moving_vehicles() - { - let pl = r.shift_from_left_side(width).unwrap(); - batch.extend( - center_line_color, - pl.dashed_lines( - Distance::meters(0.25), - Distance::meters(2.0), - Distance::meters(1.0), - ), - ); - } - } - - batch - } - pub fn render>(&self, prerender: &P, app: &dyn AppLike) -> GeomBatch { let prerender = prerender.as_ref(); let r = app.map().get_r(self.id); - let center_line_color = if r.is_private() && app.cs().private_road.is_some() { - app.cs() - .road_center_line - .lerp(app.cs().private_road.unwrap(), 0.5) - } else { - app.cs().road_center_line - }; - let mut batch = self.render_center_line(app); + if r.is_light_rail() { + // No label or center-line + return GeomBatch::new(); + } + let name = r.get_name(app.opts().language.as_ref()); + let mut batch; + if r.length() >= Distance::meters(30.0) && name != "???" { + // Render a label, so split the center-line into two pieces + let text_width = + Distance::meters(Text::from(&name).rendered_width(prerender) * LABEL_SCALE_FACTOR); + batch = render_center_line(app, r, Some(text_width)); - // Draw the label - if !r.is_light_rail() { - let name = r.get_name(app.opts().language.as_ref()); - if r.length() >= Distance::meters(30.0) && name != "???" { - // TODO If it's definitely straddling bus/bike lanes, change the color? Or - // even easier, just skip the center lines? - let bg = if r.is_private() && app.cs().private_road.is_some() { + if DRAW_CURVEY_LABEL { + let fg = Color::WHITE; + if r.center_pts.quadrant() > 1 && r.center_pts.quadrant() < 4 { + batch.append(Line(name).fg(fg).outlined(Color::BLACK).render_curvey( + prerender, + &r.center_pts.reversed(), + LABEL_SCALE_FACTOR, + )); + } else { + batch.append(Line(name).fg(fg).outlined(Color::BLACK).render_curvey( + prerender, + &r.center_pts, + LABEL_SCALE_FACTOR, + )); + } + } else { + let center_line_color = if r.is_private() && app.cs().private_road.is_some() { app.cs() - .zoomed_road_surface(LaneType::Driving, r.get_rank()) + .road_center_line .lerp(app.cs().private_road.unwrap(), 0.5) } else { - app.cs() - .zoomed_road_surface(LaneType::Driving, r.get_rank()) + app.cs().road_center_line }; - // TODO: find a good way to draw an appropriate background - if false { - if r.center_pts.quadrant() > 1 && r.center_pts.quadrant() < 4 { - batch.append(Line(name).fg(center_line_color).render_curvey( - prerender, - &r.center_pts.reversed(), - 0.1, - )); - } else { - batch.append(Line(name).fg(center_line_color).render_curvey( - prerender, - &r.center_pts, - 0.1, - )); - } - } else { - let txt = Text::from(Line(name).fg(center_line_color)).bg(bg); - let (pt, angle) = r.center_pts.must_dist_along(r.length() / 2.0); - batch.append( - txt.render_autocropped(prerender) - .scale(0.1) - .centered_on(pt) - .rotate_around_batch_center(angle.reorient()), - ); - } + let txt = Text::from(Line(name).fg(center_line_color)); + let (pt, angle) = r.center_pts.must_dist_along(r.length() / 2.0); + batch.append( + txt.render_autocropped(prerender) + .scale(LABEL_SCALE_FACTOR) + .centered_on(pt) + .rotate_around_batch_center(angle.reorient()), + ); } + } else { + // No label + batch = render_center_line(app, r, None); } // Driveways of connected buildings. These are grouped by road to limit what has to be @@ -163,6 +131,65 @@ impl Renderable for DrawRoad { } } +/// If `text_width` is defined, don't draw the center line in the middle of the road for this +/// amount of space +fn render_center_line(app: &dyn AppLike, r: &Road, text_width: Option) -> GeomBatch { + let center_line_color = if r.is_private() && app.cs().private_road.is_some() { + app.cs() + .road_center_line + .lerp(app.cs().private_road.unwrap(), 0.5) + } else { + app.cs().road_center_line + }; + + let mut batch = GeomBatch::new(); + + // Draw a center line every time two driving/bike/bus lanes of opposite direction are adjacent. + let mut width = Distance::ZERO; + for pair in r.lanes.windows(2) { + width += pair[0].width; + if pair[0].dir != pair[1].dir + && pair[0].lane_type.is_for_moving_vehicles() + && pair[1].lane_type.is_for_moving_vehicles() + { + let pl = r.shift_from_left_side(width).unwrap(); + if let Some(text_width) = text_width { + // Draw dashed lines from the start of the road to where the text label begins, + // then another set of dashes from where the text label ends to the end of the road + let first_segment_distance = (pl.length() - text_width) / 2.0; + let last_segment_distance = first_segment_distance + text_width; + for slice in [ + pl.slice(Distance::ZERO, first_segment_distance), + pl.slice(last_segment_distance, pl.length()), + ] { + if let Ok((line, _)) = slice { + batch.extend( + center_line_color, + line.dashed_lines( + Distance::meters(0.25), + Distance::meters(2.0), + Distance::meters(1.0), + ), + ); + } + } + } else { + // Uninterrupted center line covering the entire road + batch.extend( + center_line_color, + pl.dashed_lines( + Distance::meters(0.25), + Distance::meters(2.0), + Distance::meters(1.0), + ), + ); + } + } + } + + batch +} + fn draw_building_driveway(app: &dyn AppLike, bldg: &Building, batch: &mut GeomBatch) { if app.opts().camera_angle == CameraAngle::Abstract || !app.opts().show_building_driveways { return; diff --git a/widgetry/src/text.rs b/widgetry/src/text.rs index d4e5ea0e9f..3809bddb30 100644 --- a/widgetry/src/text.rs +++ b/widgetry/src/text.rs @@ -311,6 +311,10 @@ impl Text { self.render(assets).get_dims() } + pub fn rendered_width>(self, assets: &A) -> f64 { + self.dims(assets.as_ref()).width + } + /// Render the text, without any autocropping. You can pass in an `EventCtx` or `GfxCtx`. pub fn render>(self, assets: &A) -> GeomBatch { let assets: &Assets = assets.as_ref(); @@ -543,6 +547,11 @@ impl TextSpan { ) -> GeomBatch { let assets = assets.as_ref(); let tolerance = svg::HIGH_QUALITY; + let mut stroke_parameters = String::new(); + + if let Some(c) = self.outline_color { + stroke_parameters = format!("stroke=\"{}\" stroke-width=\".1\"", c.as_hex()); + }; // Just set a sufficiently large view box let mut svg = r##""##.to_string(); @@ -564,13 +573,15 @@ impl TextSpan { } write!(&mut svg, "\" />").unwrap(); // We need to subtract and account for the length of the text - let start_offset = (path.length() / 2.0).inner_meters() - - (Text::from(&self.text).dims(assets).width * scale) / 2.0; + let start_offset = (path.length().inner_meters() + - scale * Text::from(&self.text).rendered_width(&assets)) + / 2.0; let fg_color = self.fg_color_for_style(&assets.style.borrow()); + write!( &mut svg, - r##""##, + r##""##, // This is seemingly the easiest way to do this. We could .scale() the whole batch // after, but then we have to re-translate it to the proper spot (self.size as f64) * scale, @@ -583,6 +594,7 @@ impl TextSpan { fg_color.as_hex(), fg_color.a, start_offset, + stroke_parameters, ) .unwrap();