Skip to content
This repository has been archived by the owner on Mar 9, 2023. It is now read-only.

Commit

Permalink
Merge pull request #215 from droogmic/cycleway_opposite
Browse files Browse the repository at this point in the history
Add Support For cycleway=opposite
  • Loading branch information
droogmic authored Jun 1, 2022
2 parents 8fb4119 + 1148512 commit a418ca7
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 60 deletions.
49 changes: 46 additions & 3 deletions data/spec-lanes.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@
},
"access": {
"type": "object",
"description": "Access to the lane. Freeform tags matching OSM way access",
"additionalProperties": {
"type": "string"
"description": "Access by mode.",
"properties": {
"bicycle": {
"$ref": "/schemas/access"
}
}
},
"markings": {
Expand Down Expand Up @@ -159,6 +161,47 @@
"const": "osm2lanes"
}
]
},
"access": {
"$id": "/schemas/access",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"description": "Legal access for a lane. Only populated if different than default",
"required": [
"access"
],
"properties": {
"access": {
"type": "string",
"description": "The access value.",
"anyOf": [
{
"const": "yes"
},
{
"const": "no"
},
{
"const": "designated"
}
]
},
"direction": {
"type": "string",
"description": "The direction of the access, if applicable.",
"anyOf": [
{
"const": "forward"
},
{
"const": "backward"
},
{
"const": "both"
}
]
}
}
}
}
}
9 changes: 4 additions & 5 deletions data/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -710,10 +710,8 @@
designated: motor_vehicle

- description: cycleway=opposite oneway=yes oneway:bicycle=no
rust: false # https://github.com/a-b-street/osm2lanes/issues/162
tags:
highway: "road"
lanes: "1"
oneway: "yes"
oneway:bicycle: "no"
cycleway: "opposite"
Expand All @@ -723,12 +721,13 @@
road:
highway: road
lanes:
- type: travel
direction: backward
designated: bicycle
- type: travel
direction: forward
designated: motor_vehicle
access:
bicycle:
access: "yes"
direction: "both"

- description: "cycleway:BACKWARD:lane=advisory oneway=yes oneway:bicycle=no"
way_id: 25745877
Expand Down
2 changes: 1 addition & 1 deletion osm-tags/src/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};

/// Access variants from <https://wiki.openstreetmap.org/wiki/Key:access#List_of_possible_values>
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum Access {
Yes,
No,
Expand Down
4 changes: 2 additions & 2 deletions osm2lanes/src/road/lane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl Printable for Direction {
// TODO: how to handle the motor_vehicle vs motorcar discussion in https://wiki.openstreetmap.org/wiki/Key:motorcar#Controversy
// TODO: separating weight class by usage?
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub struct AccessByType {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) foot: Option<AccessAndDirection>,
Expand All @@ -191,7 +191,7 @@ pub struct AccessByType {

/// Access for a given user
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub struct AccessAndDirection {
pub(crate) access: AccessTagValue,
/// Direction, if different from designated direction
Expand Down
14 changes: 9 additions & 5 deletions osm2lanes/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ pub struct TestCase {
// Metadata
/// The OSM way unique identifier
pub way_id: Option<i64>,
/// Relevant link
pub link: Option<String>,
/// Comment on test case
pub comment: Option<String>,
/// Description of test case
pub description: Option<String>,

// List as a named example in the web app
/// List as a named example in the web app, with the given name
example: Option<String>,

// Config and Locale
Expand Down Expand Up @@ -196,20 +199,21 @@ mod tests {
direction: actual_direction,
width: actual_width,
max_speed: actual_max_speed,
access: _actual_access,
access: actual_access,
},
Lane::Travel {
designated: expected_designated,
direction: expected_direction,
width: expected_width,
max_speed: expected_max_speed,
access: _expected_access,
access: expected_access,
},
) => {
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)
},
(
Lane::Parking {
Expand Down Expand Up @@ -242,7 +246,6 @@ mod tests {

impl Marking {
/// Eq where None is treaty as always equal
#[allow(clippy::unnested_or_patterns)]
fn approx_eq(&self, expected: &Self) -> bool {
self.style == expected.style
&& approx_eq(&self.color, &expected.color)
Expand Down Expand Up @@ -508,7 +511,7 @@ mod tests {
},
)
.unwrap();
let (output_road, _warnings) = output_lanes.into_filtered_road(test);
let (output_road, warnings) = output_lanes.into_filtered_road(test);
if !output_road.approx_eq(&input_road) {
test.print();
println!("From:");
Expand All @@ -521,6 +524,7 @@ mod tests {
println!("Got:");
println!(" {}", stringify_lane_types(&output_road));
println!(" {}", stringify_directions(&output_road));
println!("{}", warnings);
if stringify_lane_types(&input_road) == stringify_lane_types(&output_road)
|| stringify_directions(&input_road) == stringify_directions(&output_road)
{
Expand Down
37 changes: 33 additions & 4 deletions osm2lanes/src/transform/lanes_to_tags/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#![allow(clippy::module_name_repetitions)] // TODO: fix upstream

use celes::Country;
use osm_tags::Access;

pub use self::error::LanesToTagsMsg;
use super::{tags_to_lanes, TagsToLanesConfig};
use crate::locale::Locale;
use crate::locale::{DrivingSide, Locale};
use crate::metric::Speed;
use crate::road::{Color, Designated, Direction, Lane, Marking, Road};
use crate::road::{AccessByType, Color, Designated, Direction, Lane, Marking, Road};
use crate::tag::Tags;

#[non_exhaustive]
Expand All @@ -33,6 +34,13 @@ impl Lane {
fn is_shoulder(&self) -> bool {
matches!(self, Lane::Shoulder { .. })
}

fn access(&self) -> Option<&AccessByType> {
match self {
Self::Travel { access, .. } => access.as_ref(),
_ => None,
}
}
}

mod error {
Expand Down Expand Up @@ -159,7 +167,7 @@ pub fn lanes_to_tags(
set_shoulder(lanes, &mut tags)?;
set_pedestrian(lanes, &mut tags)?;
set_parking(lanes, &mut tags)?;
set_cycleway(lanes, &mut tags, oneway)?;
set_cycleway(lanes, &mut tags, oneway, locale)?;
set_busway(lanes, &mut tags, oneway)?;

let max_speed = get_max_speed(lanes, &mut tags)?;
Expand Down Expand Up @@ -326,7 +334,12 @@ fn set_parking(lanes: &[Lane], tags: &mut Tags) -> Result<(), LanesToTagsMsg> {
Ok(())
}

fn set_cycleway(lanes: &[Lane], tags: &mut Tags, oneway: bool) -> Result<(), LanesToTagsMsg> {
fn set_cycleway(
lanes: &[Lane],
tags: &mut Tags,
oneway: bool,
locale: &Locale,
) -> Result<(), LanesToTagsMsg> {
let left_cycle_lane: Option<&Lane> = lanes
.iter()
.take_while(|lane| !lane.is_motor())
Expand Down Expand Up @@ -356,6 +369,7 @@ fn set_cycleway(lanes: &[Lane], tags: &mut Tags, oneway: bool) -> Result<(), Lan
{
tags.checked_insert("oneway:bicycle", "no")?;
}

// indicate cycling traffic direction relative to the direction the osm way is oriented
// yes: same direction
// -1: contraflow
Expand Down Expand Up @@ -394,6 +408,21 @@ fn set_cycleway(lanes: &[Lane], tags: &mut Tags, oneway: bool) -> Result<(), Lan
tags.checked_insert("cycleway:right:width", width.val().to_string())?;
}

// Handle shared lanes
//if lanes.forward_inside() // TODO: this needs to exist...
if lanes.len() == 1 {
let lane = match locale.driving_side {
DrivingSide::Right => lanes.last(),
DrivingSide::Left => lanes.first(),
};
if let Some(bicycle) = lane.and_then(Lane::access).and_then(|a| a.bicycle.as_ref()) {
if oneway && bicycle.access == Access::Yes && bicycle.direction == Some(Direction::Both)
{
tags.checked_insert("cycleway", "opposite")?;
}
}
}

Ok(())
}

Expand Down
Loading

0 comments on commit a418ca7

Please sign in to comment.