diff --git a/Makefile b/Makefile index 6e05756..ee206b4 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/R/functions.R b/R/functions.R index 1e9d050..c24604a 100644 --- a/R/functions.R +++ b/R/functions.R @@ -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] diff --git a/cycling_route_analysis_brouter.Rmd b/cycling_route_analysis_brouter.Rmd index c16444e..8934fad 100644 --- a/cycling_route_analysis_brouter.Rmd +++ b/cycling_route_analysis_brouter.Rmd @@ -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’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’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() diff --git a/docker/brouter/safety.brf b/docker/brouter/safety.brf new file mode 100644 index 0000000..e29792a --- /dev/null +++ b/docker/brouter/safety.brf @@ -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 )