Compare commits

..

No commits in common. "01a4270d2ba16ffe7e110117ae75f34d56c9c500" and "42c7df44fe499cbe85632e93e0ee9a3f9c4b31ec" have entirely different histories.

4 changed files with 487 additions and 220 deletions

View File

@ -31,7 +31,7 @@ brouter-container: ./docker/brouter/docker-compose.yml
brouter-data:
cd ./docker/brouter/; rm -rf ./brouter-bkup/; mv -v ./brouter/ ./brouter-bkup/; git clone https://github.com/abrensch/brouter.git
cd ./docker/brouter/; wget -i segments.csv -P ./brouter/misc/segments4/
cd ./docker/brouter/; wget https://brouter.de/brouter/profiles2/safety.brf -O ./brouter/misc/profiles2/safety.brf
cd ./docker/brouter/; cp safety.brf ./brouter/misc/profiles2/safety.brf
cd ./docker/brouter/; rm -rf ./brouter-web-bkup/; mv -v ./brouter-web/ ./brouter-web-bkup/; git clone https://github.com/nrenner/brouter-web.git
cd ./docker/brouter/brouter-web; cp keys.template.js keys.js;
cd ./docker/brouter/brouter-web; cp config.template.js config.js

View File

@ -1,7 +1,7 @@
getLTSForRoute <- function(i, route_table) {
getLTSForRoute <- function(i) {
# Filter the routes for the current student number
current_route <- route_table %>% filter(student_number == i)
current_route <- routes %>% filter(student_number == i)
# Find intersecting OBJECTIDs
intersecting_ids <- relevant_buffer$OBJECTID[lengths(st_intersects(relevant_buffer, current_route)) > 0]

View File

@ -31,7 +31,7 @@ library(jsonlite)
library(parallel)
fig.height <- 6
set.seed(1)
runLTS <- FALSE
runTLS <- TRUE
logo <- image_read(path = "other/BFW_Logo_180_x_200_transparent_background.png")
school_symbol <- image_read_svg(path = "other/school_FILL0_wght400_GRAD0_opsz24.svg")
```
@ -48,9 +48,7 @@ WI_schools <- WI_schools %>% mutate(geom = SHAPE)
```{r addresses, eval = TRUE, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
addresses <- read_csv(file="data/addresses/Addresses_Students_EastHS_2024_GeocodeResults.csv") %>%
filter(lat > 0) %>%
st_as_sf(coords=c("lon","lat"),
crs=4326,
remove = FALSE)
st_as_sf(coords=c("lon","lat"), crs=4326)
```
(Remember that x = lon and y = lat.)
@ -86,6 +84,7 @@ options(osrm.profile = "bike")
```{r brouter, eval = TRUE, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# Set url and profile of brouter server
brouter_url <- "http://127.0.0.1:17777/brouter"
brouter_profile <- "safety"
```
## Stadia Maps API Key
@ -138,17 +137,14 @@ square grid, the total number of points will be res*res. Increase res to obtain
## Calculate Routes
```{r routes, eval = TRUE, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
routes <- list(NULL)
school_focus_location <- WI_schools %>% filter(NCES_CODE %in% school_focus$NCES_CODE) %>% select(LAT, LON)
#calculate routes with the safety profile
brouter_profile <- "safety"
routes_safety <- list(NULL)
for(i in addresses_near %>% arrange(number) %>% pull(number)) {
query <- paste0(
brouter_url,
"?lonlats=",
addresses_near %>% filter(number == i) %>% pull(lon), ",",
addresses_near %>% filter(number == i) %>% pull(lat), "|",
(addresses_near %>% filter(number == i) %>% pull(point) %>% str_split(., ","))[[1]][1], ",",
(addresses_near %>% filter(number == i) %>% pull(point) %>% str_split(., ","))[[1]][2], "|",
school_focus_location$LON, ",", school_focus_location$LAT,
"&profile=", brouter_profile,
"&alternativeidx=0&format=geojson"
@ -156,117 +152,18 @@ for(i in addresses_near %>% arrange(number) %>% pull(number)) {
response <- GET(query)
route_run <- st_read(content <- content(response, as = "text"), quiet = TRUE)
route_run[["student_number"]] <- i
routes_safety[[i]] <- route_run
routes[[i]] <- route_run
message(paste0("done - ", i, " of ", max(addresses_near$number)))
}
# combine the list of routes into a data table and make sure its the right crs
routes_safety <- st_transform(bind_rows(routes_safety), crs = 4326)
# calculate routes with the "shortest" profile
brouter_profile <- "shortest"
routes_shortest <- list(NULL)
for(i in addresses_near %>% arrange(number) %>% pull(number)) {
query <- paste0(
brouter_url,
"?lonlats=",
addresses_near %>% filter(number == i) %>% pull(lon), ",",
addresses_near %>% filter(number == i) %>% pull(lat), "|",
school_focus_location$LON, ",", school_focus_location$LAT,
"&profile=", brouter_profile,
"&alternativeidx=0&format=geojson"
)
response <- GET(query)
route_run <- st_read(content <- content(response, as = "text"), quiet = TRUE)
route_run[["student_number"]] <- i
routes_shortest[[i]] <- route_run
message(paste0("done - ", i, " of ", max(addresses_near$number)))
}
# combine the list of routes into a data table and make sure its the right crs
routes_shortest <- st_transform(bind_rows(routes_shortest), crs = 4326)
routes <- bind_rows(routes_safety, routes_shortest) %>%
mutate(total.time = as.double(total.time),
total.energy = as.double(total.energy),
track.length = as.double(track.length))
routes <- st_transform(bind_rows(routes), crs = 4326)
```
Notes:
- this queries the brouter server to get routes
## plot efficiency loss
```{r plotefficiency, eval = TRUE, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
ggplot(data = as.data.frame(routes) %>%
select(student_number, name, track.length) %>%
pivot_wider(names_from = name, values_from = track.length) %>%
mutate(difference_percent = ((brouter_safety_0/brouter_shortest_0) - 1) * 100)) +
geom_point(aes(x = brouter_shortest_0 / 1609, y = difference_percent)) +
labs(x = "distance for shortest route",
y = "Percent difference between safest and shortest routes")
```
## Combine routes with Bike LTS
```{r ltscount, eval = runLTS, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# Count the routes that intersect or overlap with each segment of the bike_tls network.
# The intersections have a buffer of 10 m
bike_lts_buffer <- st_buffer(st_intersection(bike_lts, cycle_boundary_poly), 10)
bike_lts_buffer["student_use"] <- unlist(lapply(st_intersects(bike_lts_buffer, routes_safety), length))
bike_lts_studentuse <- left_join(bike_lts, as.data.frame(bike_lts_buffer) %>% select(OBJECTID, student_use), by = "OBJECTID") %>% filter(student_use > 0)
```
Notes:
- for each segment in bike_lts, this counts how many student&rsquo;s
calculated routes intersect with it (within a 10 m buffer)
```{r functions, eval = runLTS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
source("./R/functions.R")
```
```{r routeslts, eval = runLTS, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# Start with routes_lts as a NULL list
routes_lts <- list(NULL)
# Pre-filter the bike_lts_buffer for relevant student use
relevant_buffer <- bike_lts_buffer %>% filter(student_use > 0)
routes_lts <- mclapply(addresses_near %>% arrange(number) %>% pull(number),
getLTSForRoute,
route_table = routes_safety,
mc.cores = detectCores() / 2,
mc.cleanup = TRUE,
mc.preschedule = TRUE,
mc.silent = FALSE)
routes_lts <- bind_rows(routes_lts)
```
Notes:
- for each student's route, this finds which bike_lts segment it
intersects with and calculates a max and an average level of traffic
stress (LTS). This takes a while, so it runs in parallel. There's
probably a more efficient way to do this calculation.
- see ./R/functions.R for definition of getLTSForRoute()
```{r addresslts, eval = runLTS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# Join the route lts data with the addresses data
addresses_near <- left_join(addresses_near,
routes_lts %>%
select(c("student_number", "lts_max", "lts_average", "lts_1_dist", "lts_2_dist", "lts_3_dist", "lts_4_dist")),
join_by("number"=="student_number"),
multiple = "any")
# add supplemental analysis
addresses_near <- addresses_near %>% mutate(lts_34_dist = lts_3_dist + lts_4_dist)
```
# Make Maps
## Set boundaries and get basemap
```{r basemap, eval = TRUE, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
@ -282,6 +179,74 @@ basemap <- get_stadiamap(bbox = bbox, zoom = 15, maptype = "stamen_toner_lite")
Notes:
- This chunk retrieves the base map from Stadia Maps (API key required)
## Combine routes with Bike LTS
```{r ltscount, eval = TRUE, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# Count the routes that intersect or overlap with each segment of the bike_tls network.
# The intersections have a buffer of 10m
bike_lts_buffer <- st_buffer(st_intersection(bike_lts, cycle_boundary_poly), 10)
bike_lts_buffer["student_use"] <- unlist(lapply(st_intersects(bike_lts_buffer, routes), length))
bike_lts <- left_join(bike_lts, as.data.frame(bike_lts_buffer %>% select(OBJECTID, student_use)), by = "OBJECTID")
```
Notes:
- for each segment in bike_lts, this counts how many student&rsquo;s
calculated routes intersect with it (within a 10 m buffer)
```{r functions, eval = runTLS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
source("./R/functions.R")
```
```{r routeslts, eval = runTLS, echo = TRUE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# Start with routes_lts as a NULL list
routes_lts <- list(NULL)
# Pre-filter the bike_lts_buffer for relevant student use
relevant_buffer <- bike_lts_buffer %>% filter(student_use > 0)
routes_lts <- mclapply(addresses_near %>% arrange(number) %>% pull(number),
getLTSForRoute,
mc.cores = detectCores() / 2,
mc.cleanup = TRUE,
mc.preschedule = TRUE,
mc.silent = FALSE)
routes_lts <- bind_rows(routes_lts)
```
Notes:
- for each student's route, this finds which bike_lts segment it
intersects with and calculates a max and an average level of traffic
stress (LTS). This takes a while, so a parallelized it. There's
probably a more efficient way to do this calculation.
- see ./R/functions.R for defintion of getLTSForRoute()
# Make Maps
## Generate map with LTS data
```{r maplts, eval = runTLS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
ggmap(basemap) +
geom_sf(data = routes_lts %>% filter(student_number == 6), inherit.aes = FALSE,
aes(color = route$lts,
geometry = route$geometry),
linewidth = 2) +
scale_color_manual(values = bike_lts_scale$color, name = "Bike Level of Traffic Stress")
# Join the data with the addresses data
addresses_near <- left_join(addresses_near,
routes_lts %>%
select(c("student_number", "lts_max", "lts_average", "lts_1_dist", "lts_2_dist", "lts_3_dist", "lts_4_dist")),
join_by("number"=="student_number"),
multiple = "any")
# add supplemental analysis
addresses_near <- addresses_near %>% mutate(lts_34_dist = lts_3_dist + lts_4_dist)
```
## Generate map of addresses
```{r mapaddresses, eval = TRUE, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
@ -336,7 +301,7 @@ ggsave(file = paste0("figures/",
ggmap(basemap) +
labs(title = paste0("Cycling routes for students at ",
school_focus %>% pull(name)),
subtitle = paste0("only showing the safest routes for students within the ", radius, " mile cycling boundary"),
subtitle = paste0("only showing routes within the ", radius, " mile cycling boundary"),
x = NULL,
y = NULL,
color = NULL,
@ -351,7 +316,7 @@ ggmap(basemap) +
linewidth = 1) +
scale_color_manual(values = "blue", name = NULL) +
new_scale_color() +
geom_sf(data = bike_lts_studentuse %>% filter(!is.na(student_use), student_use > 3),
geom_sf(data = bike_lts %>% filter(!is.na(student_use), student_use > 3),
inherit.aes = FALSE,
aes(linewidth = student_use),
color = "mediumvioletred",
@ -382,7 +347,7 @@ ggsave(file = paste0("figures/",
```
## Generate map of routes with LTS (1)
```{r maprouteslts, eval = runLTS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
```{r maprouteslts, eval = runTLS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# generate map
ggmap(basemap) +
labs(title = paste0("Cycling routes for students at ",
@ -402,7 +367,7 @@ ggmap(basemap) +
linewidth = 1) +
scale_color_manual(values = "blue", name = NULL) +
new_scale_color() +
geom_sf(data = bike_lts_studentuse %>% filter(!is.na(student_use), student_use > 0),
geom_sf(data = bike_lts %>% filter(!is.na(student_use), student_use > 0),
inherit.aes = FALSE,
aes(color = lts,
linewidth = student_use)) +
@ -435,15 +400,14 @@ ggsave(file = paste0("figures/",
## Generate map of routes with LTS (2)
```{r mapaddresseslts, eval = runLTS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
```{r mapaddresseslts, eval = runTLS, echo = FALSE, results = "show", warning = FALSE, error = TRUE, message = FALSE}
# generate map
ggmap(basemap) +
labs(title = paste0("Level of Traffic stress for biking for students at ",
school_focus %>% pull(name)),
subtitle = "only showing routes within the cycling boundary",
x = NULL,
y = NULL,
linewidth = "Potential student cyclists") +
y = NULL) +
theme(axis.text=element_blank(),
axis.ticks=element_blank(),
plot.caption = element_text(color = "grey")) +
@ -454,7 +418,7 @@ ggmap(basemap) +
linewidth = 1) +
scale_color_manual(values = "blue", name = NULL) +
new_scale_color() +
geom_sf(data = routes_lts %>% filter(route$student_use >= 3),
geom_sf(data = routes_lts %>% filter(route$student_use >= 4),
inherit.aes = FALSE,
aes(geometry = route$geometry,
color = route$lts,
@ -490,104 +454,6 @@ ggsave(file = paste0("figures/",
# Appendix
## Notes
### R Package sf
- Simple features or simple feature access refers to a formal standard
(ISO 19125-1:2004) that describes how objects in the real world can
be represented in computers, with emphasis on the spatial geometry
of these objects. It also describes how such objects can be stored
in and retrieved from databases, and which geometrical operations
should be defined for them.
- The standard is widely implemented in spatial databases (such as
PostGIS), commercial GIS (e.g., ESRI ArcGIS) and forms the vector
data basis for libraries such as GDAL. A subset of simple features
forms the GeoJSON standard.
- R has well-supported classes for storing spatial data (sp) and
interfacing to the above mentioned environments (rgdal, rgeos), but
has so far lacked a complete implementation of simple features,
making conversions at times convoluted, inefficient or
incomplete. The package sf tries to fill this gap, and aims at
succeeding sp in the long term.
- See source [here](https://r-spatial.github.io/sf/articles/sf1.html)
- all functions and methods in sf that operate on spatial data are
prefixed by st_, which refers to spatial type; this makes them
easily findable by command-line completion.
- Tessellation st_make_grid()
### Sandbox
#### Create a Grid Over Bikeable Area
```{r sandbox1, eval = TRUE, echo = TRUE, results = "show", warning = TRUE, error = TRUE, message = TRUE}
cellsize <- 5e-3
grid <- st_intersection(cycle_boundary_poly, st_make_grid(cycle_boundary_poly, cellsize = cellsize, what = "polygons", square = FALSE))
```
#### Compute Routes from Cell Centroid to School
```{r sandbox2, eval = TRUE, echo = TRUE, results = "show", warning = TRUE, error = TRUE, message = TRUE}
grid_pts <- st_centroid(grid)
grid_coods <- st_coordinates(grid_pts)
routes <- list(NULL)
for(i in 1:nrow(grid_coods) ) {
query <- paste0(
brouter_url,
"?lonlats=", grid_coods[i,1], ",",grid_coods[i,2], "|",
school_focus_location$LON, ",", school_focus_location$LAT,
"&profile=", brouter_profile,
"&alternativeidx=0&format=geojson"
)
response <- GET(query)
route_run <- st_read(content <- content(response, as = "text"), quiet = TRUE)
route_run[["student_number"]] <- i
routes[[i]] <- route_run
}
routes <- st_transform(bind_rows(routes), crs = 4326)
```
Notes:
- What does `st_transform(bind_rows(routes), crs = 4326)` do?
#### Generate Map for Total Time
```{r sandbox3, eval = TRUE, echo = TRUE, results = "show", warning = TRUE, error = TRUE, message = TRUE}
track.length.vec <- routes %>% pull(track.length)
grid <- cbind(grid, track.length = as.numeric(track.length.vec)/1609)
total.time.vec <- routes %>% pull(total.time)
grid <- cbind(grid, total.time = as.numeric(total.time.vec)/60)
total.energy.vec <- routes %>% pull(total.energy)
grid <- cbind(grid, total.energy = as.numeric(total.energy.vec))
ggmap(basemap) +
geom_sf(data = grid,
aes(fill = total.time),
inherit.aes = FALSE
)
```
#### Available Route Data
```{r sandbox4, eval = TRUE, echo = TRUE, results = "show", warning = TRUE, error = TRUE, message = TRUE}
attributes(routes)$names
```
##### Message Data?
What information can we pull out of the messages data?
```{r sandbox5, eval = TRUE, echo = TRUE, results = "show", warning = TRUE, error = TRUE, message = TRUE}
routes[1,"messages"]
```
```{r chunklast, eval = TRUE, echo = TRUE, results = "show", warning = TRUE, error = TRUE, message = TRUE}
date()
sessionInfo()

401
docker/brouter/safety.brf Normal file
View File

@ -0,0 +1,401 @@
# *** The trekking profile is for slow travel
# *** and avoiding car traffic, but still with
# *** a focus on approaching your destination
# *** efficiently.
---context:global # following code refers to global config
# Bike profile
assign validForBikes = true
# Use the following switches to change behaviour
assign allow_steps = true # %allow_steps% | Set false to disallow steps | boolean
assign allow_ferries = true # %allow_ferries% | Set false to disallow ferries | boolean
assign ignore_cycleroutes = false # %ignore_cycleroutes% | Set true for better elevation results | boolean
assign stick_to_cycleroutes = false # %stick_to_cycleroutes% | Set true to just follow cycleroutes | boolean
assign avoid_unsafe = true # %avoid_unsafe% | Set true to avoid standard highways | boolean
assign consider_noise = false # %consider_noise% | Activate to prefer a low-noise route | boolean
assign consider_river = false # %consider_river% | Activate to prefer a route along rivers, lakes, etc. | boolean
assign consider_forest = false # %consider_forest% | Activate to prefer a route in forest or parks | boolean
assign consider_town = false # %consider_town% | Activate to bypass cities / big towns as far as possible | boolean
assign consider_traffic = false # %consider_traffic% | Activate to consider traffic estimates | boolean
# Change elevation parameters
assign consider_elevation = true # %consider_elevation% | Set true to favor a route with few elevation meters | boolean
assign downhillcost = 60 # %downhillcost% | Cost for going downhill | number
assign downhillcutoff = 1.5 # %downhillcutoff% | Gradients below this value in percents are not counted. | number
assign uphillcost = 0 # %uphillcost% | Cost for going uphill | number
assign uphillcutoff = 1.5 # %uphillcutoff% | Gradients below this value in percents are not counted. | number
assign downhillcost = if consider_elevation then downhillcost else 0
assign uphillcost = if consider_elevation then uphillcost else 0
# Kinematic model parameters (travel time computation)
assign totalMass = 90 # %totalMass% | Mass (in kg) of the bike + biker, for travel time computation | number
assign maxSpeed = 45 # %maxSpeed% | Absolute maximum speed (in km/h), for travel time computation | number
assign S_C_x = 0.225 # %S_C_x% | Drag coefficient times the reference area (in m^2), for travel time computation | number
assign C_r = 0.01 # %C_r% | Rolling resistance coefficient (dimensionless), for travel time computation | number
assign bikerPower = 100 # %bikerPower% | Average power (in W) provided by the biker, for travel time computation | number
# Turn instructions settings
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
assign turnInstructionCatchingRange = 40 # %turnInstructionCatchingRange% | Within this distance (in m) several turning instructions are combined into one and the turning angles are better approximated to the general direction | number
assign turnInstructionRoundabouts = true # %turnInstructionRoundabouts% | Set "false" to avoid generating special turning instructions for roundabouts | boolean
assign considerTurnRestrictions = true # %considerTurnRestrictions% | Set true to take turn restrictions into account | boolean
assign processUnusedTags = false # %processUnusedTags% | Set true to output unused tags in data tab | boolean
---context:way # following code refers to way-tags
# classifier constants
assign classifier_none = 1
assign classifier_ferry = 2
#
# pre-calculate some logical expressions
#
assign any_cycleroute =
if route_bicycle_icn=yes then true
else if route_bicycle_ncn=yes then true
else if route_bicycle_rcn=yes then true
else if route_bicycle_lcn=yes then true
else false
assign nodeaccessgranted =
if any_cycleroute then true
else lcn=yes
assign is_ldcr =
if ignore_cycleroutes then false
else any_cycleroute
assign isbike = or bicycle_road=yes or bicycle=yes or or bicycle=permissive bicycle=designated lcn=yes
assign ispaved = surface=paved|asphalt|concrete|paving_stones|sett
assign isunpaved = not or surface= or ispaved surface=fine_gravel|cobblestone
assign probablyGood = or ispaved and ( or isbike highway=footway ) not isunpaved
#
# this is the cost (in Meter) for a 90-degree turn
# The actual cost is calculated as turncost*cos(angle)
# (Suppressing turncost while following longdistance-cycleways
# makes them a little bit more magnetic)
#
assign turncost = if is_ldcr then 0
else if junction=roundabout then 0
else 90
#
# for any change in initialclassifier, initialcost is added once
#
assign initialclassifier =
if route=ferry then classifier_ferry
else classifier_none
#
# calculate the initial cost
# this is added to the total cost each time the costfactor
# changed
#
assign initialcost =
if ( equal initialclassifier classifier_ferry ) then 10000
else 0
#
# implicit access here just from the motorroad tag
# (implicit access rules from highway tag handled elsewhere)
#
assign defaultaccess =
if access= then not motorroad=yes
else if access=private|no then false
else true
#
# calculate logical bike access
#
assign bikeaccess =
if bicycle= then
(
if bicycle_road=yes then true
else if vehicle= then ( if highway=footway then false else defaultaccess )
else not vehicle=private|no
)
else not bicycle=private|no|dismount|use_sidepath
#
# calculate logical foot access
#
assign footaccess =
if bikeaccess then true
else if bicycle=dismount then true
else if foot= then defaultaccess
else not foot=private|no
#
# if not bike-, but foot-acess, just a moderate penalty,
# otherwise access is forbidden
#
assign accesspenalty =
if bikeaccess then 0
else if footaccess then 4
else if any_cycleroute then 15
else 10000
#
# handle one-ways. On primary roads, wrong-oneways should
# be close to forbidden, while on other ways we just add
# 4 to the costfactor (making it at least 5 - you are allowed
# to push your bike)
#
assign badoneway =
if reversedirection=yes then
if oneway:bicycle=yes then true
else if oneway= then junction=roundabout
else oneway=yes|true|1
else oneway=-1
assign onewaypenalty =
if ( badoneway ) then
(
if ( cycleway=opposite|opposite_lane|opposite_track ) then 0
else if ( cycleway:left=opposite|opposite_lane|opposite_track ) then 0
else if ( cycleway:right=opposite|opposite_lane|opposite_track ) then 0
else if ( oneway:bicycle=no ) then 0
else if ( cycleway:left:oneway=no ) then 0
else if ( cycleway:right:oneway=no ) then 0
else if ( junction=roundabout|circular ) then 60
else if ( highway=primary|primary_link ) then 50
else if ( highway=secondary|secondary_link ) then 30
else if ( highway=tertiary|tertiary_link ) then 20
else 4.0
)
else 0.0
# add estimate tags
assign traffic_penalty
switch consider_traffic
switch estimated_traffic_class= 0
switch estimated_traffic_class=1|2 0.2
switch estimated_traffic_class=3 0.4
switch estimated_traffic_class=4 0.6
switch estimated_traffic_class=5 0.8
switch estimated_traffic_class=6|7 1 99 0
assign noise_penalty
switch consider_noise
switch estimated_noise_class= 0
switch estimated_noise_class=1 0.3
switch estimated_noise_class=2 0.5
switch estimated_noise_class=3 0.8
switch estimated_noise_class=4 1.4
switch estimated_noise_class=5 1.7
switch estimated_noise_class=6 2 0 0
assign no_river_penalty
switch consider_river
switch estimated_river_class= 2
switch estimated_river_class=1 1.3
switch estimated_river_class=2 1
switch estimated_river_class=3 0.7
switch estimated_river_class=4 0.4
switch estimated_river_class=5 0.1
switch estimated_river_class=6 0 99 0
assign no_forest_penalty
switch consider_forest
switch estimated_forest_class= 1
switch estimated_forest_class=1 0.5
switch estimated_forest_class=2 0.4
switch estimated_forest_class=3 0.25
switch estimated_forest_class=4 0.15
switch estimated_forest_class=5 0.1
switch estimated_forest_class=6 0 99 0
assign town_penalty
switch consider_town
switch estimated_town_class= 0
switch estimated_town_class=1 0.5
switch estimated_town_class=2 0.9
switch estimated_town_class=3 1.2
switch estimated_town_class=4 1.3
switch estimated_town_class=5 1.4
switch estimated_town_class=6 1.6 99 0
#
# calculate the cost-factor, which is the factor
# by which the distance of a way-segment is multiplied
# to calculate the cost of that segment. The costfactor
# must be >=1 and it's supposed to be close to 1 for
# the type of way the routing profile is searching for
#
assign isresidentialorliving = or highway=residential|living_street living_street=yes
assign costfactor
#
# exclude rivers, rails etc.
#
if ( and highway= not route=ferry ) then 10000
#
# exclude motorways and proposed roads
#
else if ( highway=motorway|motorway_link ) then 10000
else if ( highway=proposed|abandoned ) then 10000
#
# all other exclusions below (access, steps, ferries,..)
# should not be deleted by the decoder, to be available
# in voice-hint-processing
#
else min 9999
add town_penalty
add no_forest_penalty
add no_river_penalty
add noise_penalty
add traffic_penalty
#
# apply oneway-and access-penalties
#
add max onewaypenalty accesspenalty
#
# steps and ferries are special. Note this is handled
# before the cycleroute-switch, to be able
# to really exlude them be setting cost to infinity
#
if ( highway=steps ) then ( if allow_steps then 40 else 10000 )
else if ( route=ferry ) then ( if allow_ferries then 5.67 else 10000 )
#
# handle long-distance cycle-routes.
#
else if ( is_ldcr ) then 1 # always treated as perfect (=1)
else
add ( if stick_to_cycleroutes then 0.5 else 0.05 ) # everything else somewhat up
#
# some other highway types
#
if ( highway=pedestrian ) then 3
else if ( highway=bridleway ) then 5
else if ( highway=cycleway ) then 1
else if ( isresidentialorliving ) then ( if isunpaved then 1.5 else 1.1 )
else if ( highway=service ) then ( if isunpaved then 1.6 else 1.3 )
#
# tracks and track-like ways are rated mainly be tracktype/grade
# But note that if no tracktype is given (mainly for road/path/footway)
# it can be o.k. if there's any other hint for quality
#
else if ( highway=track|road|path|footway ) then
(
if ( tracktype=grade1 ) then ( if probablyGood then 1.0 else 1.3 )
else if ( tracktype=grade2 ) then ( if probablyGood then 1.1 else 2.0 )
else if ( tracktype=grade3 ) then ( if probablyGood then 1.5 else 3.0 )
else if ( tracktype=grade4 ) then ( if probablyGood then 2.0 else 5.0 )
else if ( tracktype=grade5 ) then ( if probablyGood then 3.0 else 5.0 )
else ( if probablyGood then 1.0 else 5.0 )
)
#
# When avoiding unsafe ways, avoid highways without a bike hint
#
else add ( if ( and avoid_unsafe not isbike ) then 2 else 0 )
#
# actuals roads are o.k. if we have a bike hint
#
if ( highway=trunk|trunk_link ) then ( if isbike then 1.5 else 10 )
else if ( highway=primary|primary_link ) then ( if isbike then 1.2 else 3 )
else if ( highway=secondary|secondary_link ) then ( if isbike then 1.1 else 1.6 )
else if ( highway=tertiary|tertiary_link ) then ( if isbike then 1.0 else 1.4 )
else if ( highway=unclassified ) then ( if isbike then 1.0 else 1.3 )
#
# default for any other highway type not handled above
#
else 2.0
# way priorities used for voice hint generation
assign priorityclassifier =
if ( highway=motorway ) then 30
else if ( highway=motorway_link ) then 29
else if ( highway=trunk ) then 28
else if ( highway=trunk_link ) then 27
else if ( highway=primary ) then 26
else if ( highway=primary_link ) then 25
else if ( highway=secondary ) then 24
else if ( highway=secondary_link ) then 23
else if ( highway=tertiary ) then 22
else if ( highway=tertiary_link ) then 21
else if ( highway=unclassified ) then 20
else if ( isresidentialorliving ) then 6
else if ( highway=service ) then 6
else if ( highway=cycleway ) then 6
else if ( or bicycle=designated bicycle_road=yes ) then 6
else if ( highway=track ) then if tracktype=grade1 then 6 else 4
else if ( highway=bridleway|road|path|footway ) then 4
else if ( highway=steps ) then 2
else if ( highway=pedestrian ) then 2
else 0
# some more classifying bits used for voice hint generation...
assign isbadoneway = not equal onewaypenalty 0
assign isgoodoneway = if reversedirection=yes then oneway=-1
else if oneway= then junction=roundabout else oneway=yes|true|1
assign isroundabout = junction=roundabout
assign islinktype = highway=motorway_link|trunk_link|primary_link|secondary_link|tertiary_link
assign isgoodforcars = if greater priorityclassifier 6 then true
else if ( or isresidentialorliving highway=service ) then true
else if ( and highway=track tracktype=grade1 ) then true
else false
# ... encoded into a bitmask
assign classifiermask add isbadoneway
add multiply isgoodoneway 2
add multiply isroundabout 4
add multiply islinktype 8
multiply isgoodforcars 16
# include `smoothness=` tags in the response's WayTags for track analysis
assign dummyUsage = smoothness=
---context:node # following code refers to node tags
assign defaultaccess =
if ( access= ) then true # add default barrier restrictions here!
else if ( access=private|no ) then false
else true
assign bikeaccess =
if nodeaccessgranted=yes then true
else if bicycle= then
(
if vehicle= then defaultaccess
else not vehicle=private|no
)
else not bicycle=private|no|dismount
assign footaccess =
if bicycle=dismount then true
else if foot= then defaultaccess
else not foot=private|no
assign initialcost =
if bikeaccess then 0
else ( if footaccess then 100 else 1000000 )