Skip to content

Commit

Permalink
Merge pull request a-b-street#216 from droogmic/cycleway_opposite_track
Browse files Browse the repository at this point in the history
Support Cycleway Opposite Track
  • Loading branch information
droogmic authored Jun 8, 2022
2 parents c070de0 + d630c0d commit 0c8e37b
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 109 deletions.
10 changes: 7 additions & 3 deletions data/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -689,13 +689,15 @@
- type: travel
designated: foot

- description: cycleway=opposite_lane oneway=yes, deprecated
rust: false
- description: cycleway=opposite_track oneway=yes, deprecated
rust:
separator: false
expect_warnings: true
tags:
highway: "road"
lanes: "1"
oneway: "yes"
cycleway: opposite_lane
cycleway: "opposite_track"
sidewalk: "no"
shoulder: "no"
driving_side: right
Expand All @@ -705,6 +707,8 @@
- type: travel
direction: backward
designated: bicycle
- type: separator
semantic: verge
- type: travel
direction: forward
designated: motor_vehicle
Expand Down
27 changes: 25 additions & 2 deletions osm2lanes-web/src/draw.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use osm2lanes::locale::Locale;
use osm2lanes::metric::Metre;
use osm2lanes::road::{Color as MarkingColor, Designated, Direction, Lane, Printable, Road, Style};
use osm2lanes::road::{
Color as MarkingColor, Designated, Direction, Lane, Printable, Road, Semantic, Style,
};
use piet::kurbo::{Line, Point, Rect};
use piet::{
Color as PietColor, FontFamily, RenderContext, StrokeStyle, Text, TextAttribute,
Expand Down Expand Up @@ -107,9 +109,30 @@ pub(crate) fn lanes<R: RenderContext>(
rc.draw_text(&layout, (x - (0.5 * font_size), 0.5 * canvas_height));
left_edge += width;
},
Lane::Separator { markings, .. } => {
Lane::Separator {
markings: Some(markings),
..
} => {
draw_separator(rc, &mut left_edge, markings, &scale, canvas_height);
},
Lane::Separator {
markings: None,
semantic,
} => {
if let Some(Semantic::Verge) = semantic {
let width = Metre::new(0.5_f64); // TODO
rc.fill(
Rect::new(
scale.scale(left_edge),
0.0,
scale.scale(left_edge + width),
canvas_height,
),
&PietColor::GREEN,
);
left_edge += width;
}
},
}
}

Expand Down
2 changes: 1 addition & 1 deletion osm2lanes/src/metric.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde::{Deserialize, Deserializer, Serialize, Serializer};

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub struct Metre(f64);

impl Metre {
Expand Down
12 changes: 9 additions & 3 deletions osm2lanes/src/road/lane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ pub enum Lane {
Separator {
#[serde(skip_serializing_if = "Option::is_none")]
semantic: Option<Semantic>,
markings: Markings,
#[serde(skip_serializing_if = "Option::is_none")]
markings: Option<Markings>,
},
}

Expand All @@ -47,7 +48,10 @@ impl Lane {
#[must_use]
pub fn width(&self, locale: &Locale, highway: HighwayType) -> Metre {
match self {
Lane::Separator { markings, .. } => markings.width(locale),
Lane::Separator { markings, .. } => markings
.as_ref()
.map(|m| m.width(locale))
.unwrap_or_default(),
Lane::Travel {
width, designated, ..
} => width.unwrap_or_else(|| locale.travel_width(designated, highway)),
Expand All @@ -67,7 +71,9 @@ impl Lane {
mut markings,
semantic,
} => {
markings.flip();
if let Some(ref mut markings) = markings {
markings.flip();
}
Self::Separator { markings, semantic }
},
_ => self,
Expand Down
118 changes: 85 additions & 33 deletions osm2lanes/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ mod tests {

use super::*;
use crate::locale::{DrivingSide, Locale};
use crate::road::{Lane, Marking, Printable, Road};
use crate::metric::{Metre, Speed};
use crate::road::{AccessByType, Color, Lane, Marking, Markings, Printable, Road, Semantic};
use crate::tag::Highway;
use crate::transform::{
lanes_to_tags, tags_to_lanes, LanesToTagsConfig, RoadError, RoadFromTags, RoadWarnings,
Expand All @@ -156,43 +157,49 @@ mod tests {

static LOG_INIT: std::sync::Once = std::sync::Once::new();

fn approx_eq<T: PartialEq>(actual: &Option<T>, expected: &Option<T>) -> bool {
match (actual, expected) {
(None, None) | (Some(_), None) => true,
(None, Some(_)) => false,
(Some(actual), Some(expected)) => actual == expected,
trait EqExpected<Exp: ?Sized = Self> {
fn eq_exp(&self, expected: &Exp) -> bool;
}

impl<T: EqExpected> EqExpected for Option<T> {
fn eq_exp(&self, expected: &Self) -> bool {
match (self, expected) {
(None, None) | (Some(_), None) => true,
(None, Some(_)) => false,
(Some(actual), Some(expected)) => actual.eq_exp(expected),
}
}
}

impl Road {
/// Eq where None is treaty as always equal
fn approx_eq(&self, expected: &Self) -> bool {
impl EqExpected for Road {
fn eq_exp(&self, expected: &Self) -> bool {
if self.lanes.len() != expected.lanes.len() {
return false;
}
self.lanes
.iter()
.zip(expected.lanes.iter())
.all(|(actual, expected)| actual.approx_eq(expected))
.all(|(actual, expected)| actual.eq_exp(expected))
}
}

impl Lane {
/// Eq where None is treaty as always equal
fn approx_eq(&self, expected: &Self) -> bool {
impl EqExpected for Lane {
fn eq_exp(&self, expected: &Self) -> bool {
#[allow(clippy::unnested_or_patterns)]
match (self, expected) {
(
Lane::Separator {
markings: actual, ..
markings: markings_actual,
semantic: semantic_actual,
},
Lane::Separator {
markings: expected, ..
markings: markings_expected,
semantic: semantic_expected,
},
) => actual
.iter()
.zip(expected.iter())
.all(|(actual, expected)| actual.approx_eq(expected)),
) => {
markings_actual.eq_exp(&markings_expected)
&& semantic_actual.eq_exp(&semantic_expected)
},
(
Lane::Travel {
designated: actual_designated,
Expand All @@ -211,9 +218,9 @@ mod tests {
) => {
actual_designated == expected_designated
&& actual_direction == expected_direction
&& approx_eq(actual_width, expected_width)
&& approx_eq(actual_max_speed, expected_max_speed)
&& approx_eq(actual_access, expected_access)
&& actual_width.eq_exp(&expected_width)
&& actual_max_speed.eq_exp(&expected_max_speed)
&& actual_access.eq_exp(&expected_access)
},
(
Lane::Parking {
Expand All @@ -229,7 +236,7 @@ mod tests {
) => {
actual_designated == expected_designated
&& actual_direction == expected_direction
&& approx_eq(actual_width, expected_width)
&& actual_width.eq_exp(&expected_width)
},
(
Lane::Shoulder {
Expand All @@ -238,18 +245,55 @@ mod tests {
Lane::Shoulder {
width: expected_width,
},
) => approx_eq(actual_width, expected_width),
) => actual_width.eq_exp(&expected_width),
(actual, expected) => actual == expected,
}
}
}

impl Marking {
/// Eq where None is treaty as always equal
fn approx_eq(&self, expected: &Self) -> bool {
impl EqExpected for Markings {
fn eq_exp(&self, expected: &Self) -> bool {
self.iter()
.zip(expected.iter())
.all(|(actual, expected)| actual.eq_exp(expected))
}
}

impl EqExpected for Marking {
fn eq_exp(&self, expected: &Self) -> bool {
self.style == expected.style
&& approx_eq(&self.color, &expected.color)
&& approx_eq(&self.width, &expected.width)
&& self.color.eq_exp(&expected.color)
&& self.width.eq_exp(&expected.width)
}
}

impl EqExpected for Semantic {
fn eq_exp(&self, expected: &Self) -> bool {
self == expected
}
}

impl EqExpected for Metre {
fn eq_exp(&self, expected: &Self) -> bool {
self == expected
}
}

impl EqExpected for Speed {
fn eq_exp(&self, expected: &Self) -> bool {
self == expected
}
}

impl EqExpected for AccessByType {
fn eq_exp(&self, expected: &Self) -> bool {
self == expected
}
}

impl EqExpected for Color {
fn eq_exp(&self, expected: &Self) -> bool {
self == expected
}
}

Expand Down Expand Up @@ -339,7 +383,11 @@ mod tests {
.lanes
.iter()
.filter_map(|lane| {
if let Lane::Separator { markings, .. } = lane {
if let Lane::Separator {
markings: Some(markings),
..
} = lane
{
Some(
markings
.iter()
Expand Down Expand Up @@ -383,7 +431,11 @@ mod tests {
.lanes
.iter()
.filter_map(|lane| {
if let Lane::Separator { markings, .. } = lane {
if let Lane::Separator {
markings: Some(markings),
..
} = lane
{
Some(
markings
.iter()
Expand Down Expand Up @@ -432,7 +484,7 @@ mod tests {
match road_from_tags {
Ok(road_from_tags) => {
let (actual_road, warnings) = road_from_tags.into_filtered_road(test);
if actual_road.approx_eq(&expected_road) {
if actual_road.eq_exp(&expected_road) {
if test.test_expects_warnings() && warnings.is_empty() {
test.print();
println!("Expected warnings. Try removing `expect_warnings`.");
Expand Down Expand Up @@ -512,7 +564,7 @@ mod tests {
)
.unwrap();
let (output_road, warnings) = output_lanes.into_filtered_road(test);
if !output_road.approx_eq(&input_road) {
if !output_road.eq_exp(&input_road) {
test.print();
println!("From:");
println!(" {}", stringify_lane_types(&input_road));
Expand Down
2 changes: 1 addition & 1 deletion osm2lanes/src/transform/lanes_to_tags/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ fn set_parking(lanes: &[Lane], tags: &mut Tags) -> Result<(), LanesToTagsMsg> {
if let Some(Marking {
color: Some(Color::Red),
..
}) = markings.first()
}) = markings.as_ref().and_then(|m| m.first())
{
tags.checked_insert("parking:condition:both", "no_stopping")?;
}
Expand Down
Loading

0 comments on commit 0c8e37b

Please sign in to comment.