Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added lanes:forward, lanes:backward and lanes:both_ways to default tags #90

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions docs/src/defaults.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,70 @@
# Default Values
## Ways
During parsing, only `way`s with tags and nodes are considered. Further, the tags have to contain a key of either `"highway"` or `"railway"`. If the tags contain tags which have been excluded due to the `network_type` keyword (e.g. on download) are also discarded. This is relevant, if you downloaded the full network and want to generate only the `:bike` network from it.

### Highways
The returned `Way` is guaranteed to have the following `keys`:
- (`highway::String`)
- `maxspeed::DEFAULT_OSM_MAXSPEED_TYPE`
- If the tag exists before parsing, `LightOSM` attempts to parse the content to a number in km/h, while automatically converting units of `mph`. If multiple speeds are mapped, the average is returned.
- If the tag does not exist before parsing, the default value as given by `DEFAULT_MAXSPEEDS[tags["highway"]]` is returned. If there is no tag with key `"highway"` in `DEFAULT_MAXSPEEDS`, then `DEFAULT_MAXSPEEDS["other"]` is used.
- `oneway::Bool`
- `reverseway::Bool`
- `lanes::DEFAULT_OSM_LANES_TYPE`
- If the tag exists before parsing, `LightOSM` attempts to parse the content to a `DEFAULT_OSM_LANES_TYPE`
- If the tag does not exist before parsing, the default value as given by `DEFAULT_LANES_ONE_WAY[tags["highway"]]` is returned. If there is no tag with key `"highway"` in `DEFAULT_LANES_ONE_WAY`, then `DEFAULT_LANES_ONE_WAY["other"]` is used. (If the street is not `oneway`, the above discussed return values are multiplied by a factor of 2, one for every direction.)

- `lanes:forward::DEFAULT_OSM_LANES_TYPE`
- If the way is `oneway` and `reverseway` this value is 0
- If the way is `oneway` and not `reverseway` this value is parsed like `lines` but with `"lanes"=>"lanes:forward"`. If the tag `"lanes:forward"` did not exist before parsing, the value of `"lanes"` is choose instead.
- If the way is not `oneway`, the value is parsed like `lanes`, but with `"lanes"=>"lanes:forward"`, but without the factor of 2.

- `lanes:backward::DEFAULT_OSM_LANES_TYPE`
- If the way is `oneway` and not `reverseway` this value is 0
- If the way is `oneway` and `reverseway` this value is parsed like `lines` but with `"lanes"=>"lanes:backward"`. If the tag `"lanes:backward"` did not exist before parsing, the value of `"lanes"` is choose instead.
- If the way is not `oneway`, the value is parsed like `lanes`, but with `"lanes"=>"lanes:backward"`

- `lanes:both_ways::DEFAULT_OSM_LANES_TYPE`
- If the way is `oneway`, this value is 0
- If the way is not `oneway`:
- If the tag exists before parsing, `LightOSM` attempts to parse the content to a `DEFAULT_OSM_LANES_TYPE`
- If the tag does not exist before parsing, the default value as given by `DEFAULT_LANES_BOTH_WAYS[tags["highway"]]` is returned. If there is no tag with key `"highway"` in `DEFAULT_LANES_BOTH_WAYS`, `DEFAULT_LANES_BOTH_WAYS["other"]` is used.

all further tags present on the original way are preserved, but not parsed to appropriate datatypes, but rather left as `String`.

See [here](https://github.com/DeloitteOptimalReality/LightOSM.jl/blob/master/src/parse.jl#L4) for the full implementation of the `maxspeed` parsing

and [here](https://github.com/DeloitteOptimalReality/LightOSM.jl/blob/master/src/parse.jl#L56) for the full implementation of any `lanes` parsing.

See [here](https://github.com/DeloitteOptimalReality/LightOSM.jl/blob/master/src/parse.jl#L228) for the full implementation on how the final values are selected.

### Railways
The returned `Way` is guaranteed to have the following `keys`:
- (`railway::String`)
- `rail_type::String` set to `"unknown"` if not mapped
- `electrified::String` set to `"unknown"` if not mapped
- `gauge::Union{String, Nothin}` set to `nothing` if not mapped
- `usage::String` set to `"unknown"` if not mapped
- `name::String` set to `"unknown"` if not mapped
- `lanes::Union{String, Int64}` set to `1` if not mapped.
- `maxspeed::DEFAULT_OSM_MAXSPEED_TYPE`
- `oneway::DEFAULT_OSM_LANES_TYPE`
- `reverseway::DEFAULT_OSM_LANES_TYPE`

all further tags present on the original `way` are preserved, but not parsed to appropriate datatypes, but rather left as `String`.

The tags `maxspeed`, `oneway` and `reverseway` are set in the same way as described in [highways](#Highways).

## Buildings
During parsing, only `relation`s and `way`s with tags, where one of these tags has to have a key of `"building"` are considered. After parsing, the following `keys` are guaranteed to exist:
- (`building`)
- `height` (see [here](https://github.com/DeloitteOptimalReality/LightOSM.jl/blob/master/src/buildings.jl#L171) for source)

The height value is parsed via the funcitno


all further tags present in the original object are preserved, but not parsed to appropriate datatypes, but rather left as `String`.


```@docs
LightOSM.set_defaults
Expand Down
35 changes: 27 additions & 8 deletions src/constants.jl
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ const DEFAULT_MAXSPEEDS = Ref(Dict{String,DEFAULT_OSM_MAXSPEED_TYPE}(
))

"""
Default number of lanes based on highway type.
Default number of lanes based on highway type in one direction of the road.
"""
const DEFAULT_LANES = Ref(Dict{String,DEFAULT_OSM_LANES_TYPE}(
const DEFAULT_LANES_ONE_WAY = Ref(Dict{String,DEFAULT_OSM_LANES_TYPE}(
"motorway" => 3,
"trunk" => 3,
"primary" => 2,
Expand All @@ -167,6 +167,14 @@ const DEFAULT_LANES = Ref(Dict{String,DEFAULT_OSM_LANES_TYPE}(
"other" => 1
))

"""
Default number of lanes:both_ways based in highway type.
"""
const DEFAULT_LANES_BOTH_WAYS = Ref(Dict{String,DEFAULT_OSM_LANES_TYPE}(
"other" => 0
))


"""
Default oneway attribute based on highway type.
"""
Expand Down Expand Up @@ -225,8 +233,8 @@ optional.

# Keyword Arguments
- `maxspeeds::AbstractDict{String,<:Real}`: If no `maxspeed` way tag is available, these
values are used instead based on the value of the `highway` way tag. If no
`highway` way tag is available, the value for `"other"` is used. Unit is km/h.
values are used instead based on the value of the `highway` way tag. If the value of the
`highway` tag is not a key of the dictionary, the value for `"other"` is used. Unit is km/h.
Default value:
```
Dict(
Expand All @@ -241,8 +249,8 @@ optional.
)
```
- `lanes::AbstractDict{String,<:Integer}`: If no `lanes` way tag is available, these
values are used instead based on the value of the `highway` way tag. If no
`highway` way tag is available, the value for `"other"` is used.
values are used instead based on the value of the `highway` way tag. If the value of the
`highway` tag is not a key of the dictionary, the value for `"other"` is used.
Default value:
```
Dict(
Expand All @@ -256,6 +264,15 @@ optional.
"other" => 1
)
```
- `lanes_both_ways::AbstractDict{String,<:Integer}`: If no `lanes:both_ways` tag is available, these
values are used instead, based on the value of the `highway` way tag. If the value of the
`highway` tag is not a key of the dictionary, the value for `"other"` is used.
Default value:
```
Dict(
"other" => 0
)
```
- `lane_efficiency::AbstractDict{<:Integer,<:Real}`: Gives the lane efficiency based on
number of lanes. `1.0` is used for any number of lanes not specified here.
Default value:
Expand All @@ -281,13 +298,15 @@ optional.
"""
function set_defaults(;
maxspeeds::AbstractDict{String,<:Real}=DEFAULT_MAXSPEEDS[],
lanes::AbstractDict{String,<:Integer}=DEFAULT_LANES[],
lanes::AbstractDict{String,<:Integer}=DEFAULT_LANES_ONE_WAY[],
lanes_both_ways::AbstractDict{String,<:Integer}=DEFAULT_LANES_BOTH_WAYS[],
lane_efficiency::AbstractDict{<:Integer,<:Real}=LANE_EFFICIENCY[],
building_height_per_level::Real=DEFAULT_BUILDING_HEIGHT_PER_LEVEL[],
max_building_levels::Integer=DEFAULT_MAX_BUILDING_LEVELS[]
)
DEFAULT_MAXSPEEDS[] = maxspeeds
DEFAULT_LANES[] = lanes
DEFAULT_LANES_ONE_WAY[] = lanes
DEFAULT_LANES_BOTH_WAYS[] = lanes_both_ways
LANE_EFFICIENCY[] = lane_efficiency
DEFAULT_BUILDING_HEIGHT_PER_LEVEL[] = building_height_per_level
DEFAULT_MAX_BUILDING_LEVELS[] = max_building_levels
Expand Down
17 changes: 16 additions & 1 deletion src/graph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,22 @@ function add_weights!(g::OSMGraph, weight_type::Symbol=:distance)
if weight_type == :time
weight = dist / maxspeed
else
lanes = g.ways[highway].tags["lanes"]::DEFAULT_OSM_LANES_TYPE
if g.ways[highway].tags["oneway"]::Bool
lanes = g.ways[highway].tags["lanes"]::DEFAULT_OSM_LANES_TYPE
else
nodes = g.ways[highway].nodes
source_indices = findall(==(edge[1]), nodes)
if nodes[1] == nodes[end]
dest_indices = mod1.(source_indices.+1, length(nodes))
else
dest_indices = [i+1 for i in source_indices if i < length(nodes)]
end
if edge[2] in nodes[dest_indices]
lanes = g.ways[highway].tags["lanes:forward"]
else
lanes = g.ways[highway].tags["lanes:backward"]
end
end
lane_efficiency = get(LANE_EFFICIENCY[], lanes, 1.0)
weight = dist / (maxspeed * lane_efficiency)
end
Expand Down
70 changes: 63 additions & 7 deletions src/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,26 @@ function maxspeed(tags::AbstractDict)::DEFAULT_OSM_MAXSPEED_TYPE
end

"""
Determine number of lanes given osm way tags dictionary.
get correct DEFAULT_LANES dictionary based on the passed in tag
"""
function appropriate_lane_source(lanetag::AbstractString)::AbstractDict
if lanetag == "lanes"
return DEFAULT_LANES_ONE_WAY[]
elseif lanetag == "lanes:forward" || lanetag == "lanes:backward"
return DEFAULT_LANES_ONE_WAY[]
elseif lanetag == "lanes:both_ways"
return DEFAULT_LANES_BOTH_WAYS[]
else
throw(ErrorException("$lanetag does not have an associated default dictionary"))
end
end


"""
Determine number of lanes, given osm way tags dictionary and tag which contains said lanes
"""
function lanes(tags::AbstractDict)::DEFAULT_OSM_LANES_TYPE
lanes = get(tags, "lanes", "default")
function parse_lanes(tags::AbstractDict, lanetag::AbstractString)::DEFAULT_OSM_LANES_TYPE
lanes = get(tags, lanetag, "default")
U = DEFAULT_OSM_LANES_TYPE

if lanes != "default"
Expand All @@ -51,14 +67,16 @@ function lanes(tags::AbstractDict)::DEFAULT_OSM_LANES_TYPE
lanes = [remove_non_numeric(l) for l in lanes]
return U(round(mean(lanes)))
else
throw(ErrorException("Lanes is neither a string nor number, check data quality: $lanes"))
throw(ErrorException("$lanetag is neither a string nor number, check data quality: $lanes"))
end
else
highway_type = get(tags, "highway", "other")
key = getkey(DEFAULT_LANES[], highway_type, "other")
return U(DEFAULT_LANES[][key])
default_lane_source = appropriate_lane_source(lanetag)
key = getkey(default_lane_source, highway_type, "other")
return U(default_lane_source[key])
end
end



"""
Expand Down Expand Up @@ -209,9 +227,47 @@ function parse_osm_network_dict(osm_network_dict::AbstractDict, network_type::Sy
tags = way["tags"]
if is_highway(tags) && matches_network_type(tags, network_type)
tags["maxspeed"] = maxspeed(tags)
tags["lanes"] = lanes(tags)
tags["oneway"] = is_oneway(tags)
tags["reverseway"] = is_reverseway(tags)

# get values from tags, regardless if the exist
lanes = parse_lanes(tags, "lanes")
lanes_forward = parse_lanes(tags, "lanes:forward")
lanes_backward = parse_lanes(tags, "lanes:backward")
lanes_both_ways = parse_lanes(tags, "lanes:both_ways")

if tags["oneway"]
if tags["reverseway"]
lanes_forward = DEFAULT_OSM_LANES_TYPE(0)
lanes_backward = haskey(tags, "lanes:backward") ? lanes_backward : lanes
else
lanes_forward = haskey(tags, "lanes:forward") ? lanes_forward : lanes
lanes_backward = DEFAULT_OSM_LANES_TYPE(0)
end
lanes_both_ways = DEFAULT_OSM_LANES_TYPE(0)
else # if street is both ways
lanes = haskey(tags, "lanes") ? lanes : DEFAULT_OSM_LANES_TYPE(2*lanes)
if !(haskey(tags, "lanes:forward") && haskey(tags, "lanes:backward")) && (lanes % 2 == 0)
lanes_forward = DEFAULT_OSM_LANES_TYPE(lanes/2)
lanes_backward = DEFAULT_OSM_LANES_TYPE(lanes/2)
elseif lanes == 1
lanes_forward = DEFAULT_OSM_LANES_TYPE(0)
lanes_backward = DEFAULT_OSM_LANES_TYPE(0)
lanes_both_ways = DEFAULT_OSM_LANES_TYPE(1)
end
end

# check integrity
if lanes != lanes_forward + lanes_backward + lanes_both_ways
@warn "the number of lanes ($lanes) on way with id $(way["id"]) does not match the total number of lanes:forward ($lanes_forward), lanes:backward ($lanes_backward) and lanes:both_ways ($lanes_both_ways)"
end

# actually updating the values of the tags
tags["lanes"] = lanes
tags["lanes:forward"] = lanes_forward
tags["lanes:backward"] = lanes_backward
tags["lanes:both_ways"] = lanes_both_ways

nds = way["nodes"]
union!(highway_nodes, nds)
id = way["id"]
Expand Down
6 changes: 3 additions & 3 deletions test/constants.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ end

# Get original defaults
original_maxspeeds = deepcopy(LightOSM.DEFAULT_MAXSPEEDS[])
original_lanes = deepcopy(LightOSM.DEFAULT_LANES[])
original_lanes = deepcopy(LightOSM.DEFAULT_LANES_ONE_WAY[])

# Create graph using originals
original_g = LightOSM.graph_from_object(deepcopy(data); graph_type=:static, weight_type=:lane_efficiency)
Expand Down Expand Up @@ -76,6 +76,6 @@ end
# "surface": "asphalt"
@test original_g.ways[217499573].tags["maxspeed"] == original_maxspeeds["secondary"]
@test new_g.ways[217499573].tags["maxspeed"] == new_maxspeeds["secondary"]
@test original_g.ways[217499573].tags["lanes"] == original_lanes["secondary"]
@test new_g.ways[217499573].tags["lanes"] == new_lanes["secondary"]
@test original_g.ways[217499573].tags["lanes"] == 2*original_lanes["secondary"]
@test new_g.ways[217499573].tags["lanes"] == 2*new_lanes["secondary"]
end
8 changes: 4 additions & 4 deletions test/stub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ function basic_osm_graph_stub(weight_type=:distance, graph_type=:static)
[1008, 1007],
]
tag_dicts = [
Dict{String, Any}("oneway" => false, "reverseway" => false, "maxspeed" => Int16(50), "lanes" => Int8(2)),
Dict{String, Any}("oneway" => false, "reverseway" => false, "maxspeed" => Int16(100), "lanes" => Int8(4)),
Dict{String, Any}("oneway" => false, "reverseway" => false, "maxspeed" => Int16(50), "lanes" => Int8(2)),
Dict{String, Any}("oneway" => true, "reverseway" => false, "maxspeed" => Int16(50), "lanes" => Int8(1)),
Dict{String, Any}("oneway" => false, "reverseway" => false, "maxspeed" => Int16(50), "lanes" => Int8(4), "lanes:forward"=>Int8(2), "lanes:backward"=>Int8(2), "lanes:both_ways"=>Int8(0)),
Dict{String, Any}("oneway" => false, "reverseway" => false, "maxspeed" => Int16(100), "lanes" => Int8(8), "lanes:forward"=>Int8(3), "lanes:backward"=>Int8(3), "lanes:both_ways"=>Int8(2)),
Dict{String, Any}("oneway" => false, "reverseway" => false, "maxspeed" => Int16(50), "lanes" => Int8(4), "lanes:forward"=>Int8(3), "lanes:backward"=>Int8(1), "lanes:both_ways"=>Int8(0)),
Dict{String, Any}("oneway" => true, "reverseway" => false, "maxspeed" => Int16(50), "lanes" => Int8(1), "lanes:forward"=>Int8(1), "lanes:backward"=>Int8(0), "lanes:both_ways"=>Int8(0)),
]
ways = Dict(way_id => Way(way_id, nodes, tag_dict) for (way_id, nodes, tag_dict) in zip(way_ids, way_nodes, tag_dicts))

Expand Down