diff --git a/CMakeLists.txt b/CMakeLists.txt index 008bd55..9239071 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,21 +17,10 @@ enable_testing() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build") - -find_package(OpenMP) -if (OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") -endif() - - # set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake -if(OPENMP_FOUND) - set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic") -else() - message(WARNING "Configuring without OpenMP!") - set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic") -endif() +set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic") +set(CMAKE_CXX_FLAGS_SANITIZE "-Og -g -fsanitize=address -fsanitize=leak -fsanitize=undefined -DLOGLEVEL=3 -DPFAEDLE_DBG=1") +set(CMAKE_CXX_FLAGS_PROFILE "-g -pg -DLOGLEVEL=3 -DPFAEDLE_DBG=1") set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DLOGLEVEL=3 -DPFAEDLE_DBG=1") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") @@ -81,14 +70,7 @@ add_subdirectory(src) # tests add_test("utilTest" utilTest) - -# custom eval target - -add_custom_target( - eval - COMMAND make - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval -) +add_test("pfaedleTest" pfaedleTest) # handles install target install( @@ -99,3 +81,8 @@ install( FILES build/pfaedle DESTINATION bin PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries ) + +install( + FILES build/shapevl DESTINATION bin + PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries +) diff --git a/README.md b/README.md index 2694004..6666430 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Status](https://travis-ci.org/ad-freiburg/pfaedle.svg?branch=master)](https://tr # pfaedle Precise OpenStreetMap (OSM) map-matching for public transit schedules ([GTFS](https://developers.google.com/transit/gtfs/reference/) data). -Implementation and evaluation code for our paper [Sparse Map-Matching in Public Transit Networks with Turn Restrictions](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf). ## Requirements @@ -96,48 +95,8 @@ The following flags may be useful for debugging: * `-T ` only calculate shape for a single trip (specified via its GTFS trip id) and output it as GeoJSON to `/path.json` * `--write-graph` write the graph used for routing as GeoJSON to - `/graph.json` - * `--write-cgraph` if `-T` is set, write the combination graph used for - routing as GeoJSON to `/combgraph.json` * `--write-trgraph` write the complete network graph to `/trgraph.json` # Configuration A default configuration file `pfaedle.cfg` can be found in this repo and will be installed with `make install`. Custom configuration files can be specified with the `-c` flag. If no `-c` flag is set, `pfaedle` will parse and merge the following cfg files in the given order (if present): `/etc/pfaedle/pfaedle.cfg`, `$HOME/.config/pfaedle/pfaedle.cfg`, `/pfaedle.cfg`. Values given in later files will overwrite earlier defined values. - -# Evaluation - -You may run an entire evaluation of our testing datasets Vitoria-Gasteiz, Paris, Switzerland and -Stuttgart with - -``` -mkdir build && cd build -cmake .. -make -j -make eval -``` - -*Notes:* - * this will download, and filter, the entire OSM files for Spain and the -Stuttgart region. Make sure you have enough space left on your hard drive. - * in evaluation mode, pfaedle needs significantly more time, because the - calculation of the similarity measurements between shapes are expensive - * if you are only interested in the end results of a single dataset, run - `make .lighteval` in `/eval`. For example, `make paris.lighteval` - generates a shaped version of the paris dataset, without doing extensive - comparisons to the ground truth. - * similarily, if you want to run the extensive evaluation for a single dataset, - run `make .eval` in `/eval`. - - -## Evaluation requirements - - * zlib - -On Debianesque systems, type - -``` -sudo apt-get install zlib1g-dev -``` - -to install the dependencies. diff --git a/eval/Makefile b/eval/Makefile deleted file mode 100644 index 1cae7a8..0000000 --- a/eval/Makefile +++ /dev/null @@ -1,133 +0,0 @@ -EVAL_DF_BINS=10,20,30,40,50,60,70,80,90,100 - -all: eval lighteval - -lighteval: vitoria.lighteval stuttgart.lighteval paris.lighteval switzerland.lighteval - -eval: vitoria.eval stuttgart.eval paris.eval switzerland.eval - -clean: - @rm -f *.eval - @rm -rf gtfs - @rm -rf osm - @rm -rf evalout - -osmconvert: - @echo `date +"[%F %T.%3N]"` "EVAL : Fetching osmconvert..." - @curl http://m.m.i24.cc/osmconvert.c | cc -x c - -lz -O3 -o osmconvert - -%.lighteval: osm/%.osm gtfs/%/stops.txt gtfs/%/stop_times.txt gtfs/%/trips.txt gtfs/%/routes.txt eval.cfg - @echo `date +"[%F %T.%3N]"` "EVAL : Running light (without stats) evaluation for '"$*"'..." - @mkdir -p gtfs/$*/shaped - @rm -f gtfs/$*/shaped/* - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg -o gtfs/$*/shaped -D -m all 2>&1 | tee $@ - -%.eval: osm/%.osm gtfs/%/stops.txt gtfs/%/stop_times.txt gtfs/%/trips.txt gtfs/%/routes.txt eval.cfg eval-wo-osm-line-rels.cfg - @echo `date +"[%F %T.%3N]"` "EVAL : Running evaluation for '"$*"'..." - @mkdir -p gtfs/$*/shaped - @rm -f gtfs/$*/shaped/* - @mkdir -p evalout/ - @mkdir -p evalout/$*/ - @mkdir -p evalout/$*/hmm+osm - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg --eval-path evalout/$*/hmm+osm -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/hmm+osm/ -name "*.json" -print0 | xargs -0 rm - - @mkdir -p evalout/$*/greedy - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg --method greedy --eval-path evalout/$*/greedy -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/greedy/ -name "*.json" -print0 | xargs -0 rm - - @mkdir -p evalout/$*/greedy2 - @../build/pfaedle -x $< -i gtfs/$* -c eval.cfg --method greedy2 --eval-path evalout/$*/greedy2 -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/greedy2/ -name "*.json" -print0 | xargs -0 rm - - @mkdir -p evalout/$*/hmm - @../build/pfaedle -x $< -i gtfs/$* -c eval-wo-osm-line-rels.cfg --eval-path evalout/$*/hmm -o gtfs/$*/shaped -D -m all --eval --eval-df-bins $(EVAL_DF_BINS) 2>&1 | tee $@ - @find evalout/$*/hmm/ -name "*.json" -print0 | xargs -0 rm - -osm/spain-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for Spain..." - @curl --progress-bar http://download.geofabrik.de/europe/spain-latest.osm.pbf > $@ - -osm/spain-latest.osm: osm/spain-latest.osm.pbf osmconvert - @# pre-filter to vitoria gasteiz - @echo `date +"[%F %T.%3N]"` "EVAL : Pre-filtering OSM data to Vitoria-Gasteiz..." - @osmconvert -b=-2.8661,42.7480,-2.4788,43.0237 $< > $@ - -osm/baden-wuerttemberg-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for Baden-Württemberg..." - @curl --progress-bar http://download.geofabrik.de/europe/germany/baden-wuerttemberg-latest.osm.pbf > $@ - -osm/baden-wuerttemberg-latest.osm: osm/baden-wuerttemberg-latest.osm.pbf osmconvert - @echo `date +"[%F %T.%3N]"` "EVAL : Extracting OSM data..." - @osmconvert $< > $@ - -osm/france-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for France..." - @curl --progress-bar http://download.geofabrik.de/europe/france-latest.osm.pbf > $@ - -osm/paris-latest.osm: osm/france-latest.osm.pbf osmconvert - @# pre-filter to greater ile de france - @echo `date +"[%F %T.%3N]"` "EVAL : Pre-filtering OSM data to Île-de-France..." - @osmconvert -b=0.374,47.651,4.241,50.261 $< > $@ - -osm/europe-latest.osm.pbf: - @mkdir -p osm - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading OSM data for Europe..." - @curl --progress-bar http://download.geofabrik.de/europe-latest.osm.pbf > $@ - -osm/switzerland-latest.osm: osm/europe-latest.osm.pbf osmconvert - @# pre-filter to greater switzerland - @echo `date +"[%F %T.%3N]"` "EVAL : Pre-filtering OSM data to Switzerland..." - @osmconvert -b=3.757,44.245,15.579,52.670 $< > $@ - -gtfs/vitoria/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Vitoria-Gasteiz..." - @mkdir -p gtfs - @mkdir -p gtfs/vitoria - @curl --progress-bar https://transitfeeds.com/p/tuvisa-euskotran/239/latest/download > gtfs/vitoria/gtfs.zip - @cd gtfs/vitoria && unzip -qq -o gtfs.zip - @rm gtfs/vitoria/gtfs.zip - -gtfs/stuttgart/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Stuttgart..." - @mkdir -p gtfs - @mkdir -p gtfs/stuttgart - @echo "******************************************************************" - @echo "* A password is required to access the VVS dataset. Send a mail *" - @echo "* to brosi@cs.informatik.uni-freiburg.de to receive the password. " - @echo "******************************************************************" - @curl --progress-bar http://www.vvs.de/download/opendata/VVS_GTFS.zip -su vvsopendata01 > gtfs/stuttgart/gtfs.zip - @cd gtfs/stuttgart && unzip -qq -o gtfs.zip - @rm gtfs/stuttgart/gtfs.zip - -gtfs/paris/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Paris..." - @mkdir -p gtfs - @mkdir -p gtfs/paris - @curl --progress-bar https://transitfeeds.com/p/stif/822/latest/download > gtfs/paris/gtfs.zip - @cd gtfs/paris && unzip -qq -o gtfs.zip - @rm gtfs/paris/gtfs.zip - -gtfs/switzerland/%.txt: - @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Switzerland..." - @mkdir -p gtfs - @mkdir -p gtfs/switzerland - @curl --progress-bar http://gtfs.geops.ch/dl/gtfs_complete.zip > gtfs/switzerland/gtfs.zip - @cd gtfs/switzerland && unzip -qq -o gtfs.zip - @rm gtfs/switzerland/gtfs.zip - - -osm/vitoria.osm: osm/spain-latest.osm gtfs/vitoria/stops.txt gtfs/vitoria/trips.txt gtfs/vitoria/routes.txt gtfs/vitoria/stop_times.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/vitoria/ -c eval.cfg -m all -X $@ - -osm/stuttgart.osm: osm/baden-wuerttemberg-latest.osm gtfs/stuttgart/stops.txt gtfs/stuttgart/trips.txt gtfs/stuttgart/routes.txt gtfs/stuttgart/stop_times.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/stuttgart/ -c eval.cfg -m all -X $@ - -osm/paris.osm: osm/paris-latest.osm gtfs/paris/stops.txt gtfs/paris/trips.txt gtfs/paris/routes.txt gtfs/paris/stop_times.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/paris/ -c eval.cfg -m all -X $@ - -osm/switzerland.osm: osm/switzerland-latest.osm gtfs/switzerland/stops.txt eval.cfg - @../build/pfaedle -x $< -i gtfs/switzerland/ -c eval.cfg -m all -X $@ diff --git a/eval/eval-wo-osm-line-rels.cfg b/eval/eval-wo-osm-line-rels.cfg deleted file mode 100644 index e37f2b8..0000000 --- a/eval/eval-wo-osm-line-rels.cfg +++ /dev/null @@ -1,987 +0,0 @@ -# Copyright 2018, University of Freiburg -# Chair of Algorithms and Datastructures -# Authors: Patrick Brosi - -[rail] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - railway=rail - railway=light_rail - railway=narrow_gauge - route=rail - route=train - public_transport=stop_area|rel_flat - -osm_filter_lvl1: - usage=branch - -osm_filter_lvl2: - -osm_filter_lvl3: - service=crossover - service=siding - # we cannot completely drop service=yard, because it is often used - # incorrectly for crossovers - service=yard - -osm_filter_lvl4: - -osm_filter_lvl5: - usage=industrial - usage=military - usage=test - service=spur - railway:traffic_mode=freight - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - railway=abandoned - railway=construction - railway=disused - railway=miniature - railway=signal - railway=razed - railway=proposed - metro=yes - area=yes - # access=no - type=multipolygon - railway=platform - public_transport=platform - building=yes - building=train_station - amenity=shelter - amenity=bus_station - building=roof - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - railway:preferred_direction=forward - -osm_filter_oneway_reverse: - railway:preferred_direction=backward - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=no - oneway=-1 - railway:preferred_direction=both - railway:bidirectional=regular - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - railway=stop - railway=halt - railway=station - #railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - [public_transport=stop_area]uic_ref=500 - [public_transport=stop_area]wikidata=500 - name=100 - [public_transport=stop_area]name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -# max edge level to which station will be snapped -osm_max_snap_level: 2 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - [public_transport=stop_area]name - uic_name - -# the track number tag in edges, first match is taken -osm_edge_track_number_tags: - railway:track_ref - local_ref - ref - -# the track number tag in stop nodes, first match is taken, -# overwrites osm_edge_track_number_tags -osm_track_number_tags: - local_ref - ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 2 -routing_lvl4_fac: 2.5 -routing_lvl5_fac: 3.5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 7 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 3000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 100 - -routing_platform_unmatched_punish: 2000 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 100 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 100 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 1 - -# special line normalization for trains -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - ç -> c; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS with whitespace (ICE101 -> ICE 101) - ^([a-zA-Z]+)([0-9]+)$ -> \1 \2; - - # if a character line number is present, delete the numeric part - ^([a-zA-Z]+) [0-9]+$ -> \1; - -[bus] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - # highways - highway=motorway - highway=trunk - highway=primary - highway=secondary - highway=tertiary - highway=residential - highway=living_street - highway=unclassified - - # highway links - highway=motorway_link - highway=trunk_link - highway=primary_link - highway=secondary_link - highway=tertiary_link - highway=residential_link - - way=primary - way=seconday - way=bus_guideway - highway=bus_guideway - busway=* - psv=yes - psv=designated - - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - - route=bus - route=trolleybus - bus=yes - bus=designated - minibus=designated - minibus=yes - - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station|no_match_ways|no_match_rels - - # relations for the restriction system - type=restriction - type=restriction:bus - type=restriction:motorcar - -osm_filter_lvl1: - highway=secondary - highway=secondary_link - bus=yes - bus=designated - minibus=yes - minibus=designated - psv=designated - psv=yes - access=psv - access=bus - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - psv=designated - -osm_filter_lvl2: - highway=tertiary - highway=tertiary_link - -osm_filter_lvl3: - highway=unclassified - highway=residential - highway=road - -osm_filter_lvl4: - highway=living_street - highway=pedestrian - highway=service - psv=no - -osm_filter_lvl5: - bus=no - service=siding - access=permissive - access=private - access=no - service=parking_aisle - highway=footway - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - train=yes|no_match_ways - # access=no - public_transport=stop_area|no_match_nds|no_match_rels - type=multipolygon - railway=platform - railway=station - # service=parking_aisle - highway=proposed - highway=footway - highway=construction - building=yes - building=train_station - leisure=garden - leisure=park - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - -# Configuration of the OSM road restriction system -# We only support restriction with a single via node -# atm - -osm_node_negative_restriction: - restriction=no_right_turn - restriction=no_left_turn - restriction=no_u_turn - restriction=no_straight_on - restriction:bus=no_right_turn - restriction:bus=no_left_turn - restriction:bus=no_u_turn - restriction:bus=no_straight_on - -osm_node_positive_restriction: - restriction=only_left_turn - restriction=only_straight_on - restriction=only_right_turn - restriction:bus=only_left_turn - restriction:bus=only_straight_on - restriction:bus=only_right_turn - -osm_filter_no_restriction: - except=psv|mult_val_match - except=bus|mult_val_match - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - junction=roundabout # oneway=yes is implied - highway=motorway # oneway=yes is implied - oneway=yes - oneway=1 - oneway=true - oneway:bus=yes - oneway:bus=1 - oneway:bus=true - oneway:psv=yes - oneway:psv=1 - oneway:psv=true - -osm_filter_oneway_reverse: - oneway=-1 - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=0 - oneway=alternating - oneway=reversible - oneway=no - oneway:bus=no - oneway:bus=0 - oneway:bus=false - oneway:psv=no - oneway:psv=0 - oneway:psv=false - busway=opposite_lane - busway=opposite - busway:left=opposite_lane - busway:right=opposite_lane - psv=opposite_lane - psv=opposite - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10 , 50, 100 - -osm_max_snap_level: 5 - -osm_max_osm_station_distance: 7.5 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 3 -routing_lvl6_fac: 4 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 500 - -routing_station_distance_punish_fac: 2.5 - -routing_non_osm_station_punish: 500 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 20 - -# Max angle in a route from a station to an already reachable neighbor -routing_snap_full_turn_angle: 110 - -osm_max_node_block_distance: 10 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 4 - -routing_one_way_edge_punish: 5000 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -# routing_line_unmatched_punish_fac: 1.75 - -[tram, subway, funicular] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=tram - railway=subway - railway=light_rail - railway=tram - railway=funicular - railway=station - railway=halt - railway=tram_stop - route=subway - route=light_rail - subway=yes - tram=yes - -osm_filter_lv2: - service=siding - -osm_filter_lvl5: - service=crossover - service=yard - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - public_transport=stop_area - type=multipolygon - railway=platform - public_transport=platform - service=alley - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=subway - station=tram - railway=stop - railway=halt - railway=station - railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 2000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 235 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 80 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 80 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[ferry] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=ferry - waterway=river - motorboat=yes - ferry=yes - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=ferry - railway=stop - railway=halt - railway=station - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 100 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 50 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 45 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 0 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[tram, bus, subway, rail, gondola, funicular, ferry] -# Regular expressions and station comparision is -# always case insensitive! -station_normalize_chain: - , -> ' '; - - -> ' '; - — -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - \( -> ' '; - \) -> ' '; - \[ -> ' '; - \] -> ' '; - / -> ' '; - '\\' -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - ô -> o; - ç -> c; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - str\. -> strasse; - av\. -> avenue; - - # always separate 'street', 'strasse' - '([a-zA-Z])strasse($| )' -> '\1 strasse\2'; - '([a-zA-Z])street($| )' -> '\1 street\2'; - - # always use "street" - '(^| )strasse($| )' -> '\1street\2'; - - # always use "avenue" - '(^| )avenida($| )' -> '\1avenue\2'; - '(^| )avenu($| )' -> '\1avenue\2'; - - # normalize every possible abbr. of german "Bahnhof", "Hauptbahnhof", "Busbahnhof" - '(^| )hauptbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf($| )' -> '\1hauptbahnhof\2'; - '(^| )zentraler busbahnhof($| )$' -> \1busbahnhof\2; - '(^| )zentraler omnibusbahnhof($| )$' -> \1busbahnhof\2; - '(^| )omnibusbahnhof($| )' -> '\1busbahnhof\2'; - '(^| )omnibusbhf($| )' -> '\1busbahnhof\2'; - '(^| )busbf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbf($| )' -> '\1busbahnhof\2'; - '(^| )bus bf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bf($| )' -> '\1busbahnhof\2'; - '(^| )busbhf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbhf($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf($| )' -> '\1busbahnhof\2'; - '(^| )zob($| )' -> '\1busbahnhof\2'; - '(^| )hbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hb\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hb($| )' -> '\1hauptbahnhof\2'; - '(^| )bf\.($| )' -> '\1bahnhof\2'; - '(^| )bf($| )' -> '\1bahnhof\2'; - '(^| )bhf\.($| )' -> '\1bahnhof\2'; - '(^| )bhf($| )' -> '\1bahnhof\2'; - '(^| )bhfeingang($| )' -> '\1bahnhofeingang\2'; - '(^| )gare de($| )' -> '\1gare\2'; - - - # if a stations starts with single station identifier - # always put it at the end (for example, "hauptbahnhof freiburg" becomes "freiburg hauptbahnhof") - '^hauptbahnhof (.+)$' -> \1 hauptbahnhof; - '^bahnhof (.+)$' -> \1 bahnhof; - '^busbahnhof (.+)$' -> \1 busbahnhof; - '^gare (.+)$' -> \1 gare; - '^station (.+)$' -> \1 station; - - '(^| )busbahnhof($| )' -> '\1bbahnhof\2'; - - # normalize line types in station names - '(^| )u bahn\.($| )' -> '\1ubahn\2'; - '(^| )metro\.($| )' -> '\1ubahn\2'; - '(^| )subway\.($| )' -> '\1ubahn\2'; - '(^| )underground\.($| )' -> '\1ubahn\2'; - '(^| )ubahn($| )' -> '\1u\2'; - '(^| )s bahn\.($| )' -> '\1sbahn\2'; - '(^| )sbahn($| )' -> '\1s\2'; - '(^| )tramway($| )' -> '\1tram\2'; - '(^| )stadtbahn($| )' -> '\1tram\2'; - '(^| )strassenbahn($| )' -> '\1tram\2'; - '(^| )streetcar($| )' -> '\1tram\2'; - '(^| )tram($| )' -> '\1t\2'; - - # delete track information from name - '(^| )kante [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )gleis [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )track [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )voie [a-zA-Z0-9]{1,2}($| )' -> ' '; - - # abbrv - '(^| )und($| )' -> '\1+\2'; - '(^| )and($| )' -> '\1+\2'; - '(^| )et($| )' -> '\1+\2'; - - # noise - '\sde\s' -> ' '; - '\sda\s' -> ' '; - '\sdi\s' -> ' '; - '\sdel\s' -> ' '; - '\sdal\s' -> ' '; - - # abbrv in most western languages - '(^| )saint ' -> '\1st. '; - '(^| )sankt ' -> '\1st. '; - '(^| )sanct ' -> '\1st. '; - - \. -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - ç -> c; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS without whitespace (T 2 -> T2) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - -track_normalize_chain: - '(^| )gleis($| )' -> ''; - '(^| )gl\.($| )' -> ''; - '(^| )platform($| )' -> ''; - '(^| )track($| )' -> ''; - '(^| )rail($| )' -> ''; - # line/number combs ALWAYS without whitespace (1 A -> 1A) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - ^([0-9]+) ([a-zA-Z]+)$ -> \1\2; - - # delete track numbers greater than 999 - ^[0-9]{4,}$ -> ''; diff --git a/eval/eval.cfg b/eval/eval.cfg deleted file mode 100644 index ab4189c..0000000 --- a/eval/eval.cfg +++ /dev/null @@ -1,1002 +0,0 @@ -# Copyright 2018, University of Freiburg -# Chair of Algorithms and Datastructures -# Authors: Patrick Brosi - -[rail] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - railway=rail - railway=light_rail - railway=narrow_gauge - route=rail - route=train - public_transport=stop_area|rel_flat - -osm_filter_lvl1: - usage=branch - -osm_filter_lvl2: - -osm_filter_lvl3: - service=crossover - service=siding - # we cannot completely drop service=yard, because it is often used - # incorrectly for crossovers - service=yard - -osm_filter_lvl4: - -osm_filter_lvl5: - usage=industrial - usage=military - usage=test - service=spur - railway:traffic_mode=freight - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - railway=abandoned - railway=construction - railway=disused - railway=miniature - railway=signal - railway=razed - railway=proposed - metro=yes - area=yes - # access=no - type=multipolygon - railway=platform - public_transport=platform - building=yes - building=train_station - amenity=shelter - amenity=bus_station - building=roof - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - railway:preferred_direction=forward - -osm_filter_oneway_reverse: - railway:preferred_direction=backward - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=no - oneway=-1 - railway:preferred_direction=both - railway:bidirectional=regular - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - railway=stop - railway=halt - railway=station - #railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - [public_transport=stop_area]uic_ref=500 - [public_transport=stop_area]wikidata=500 - name=100 - [public_transport=stop_area]name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -# max edge level to which station will be snapped -osm_max_snap_level: 2 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - [public_transport=stop_area]name - uic_name - -# the track number tag in edges, first match is taken -osm_edge_track_number_tags: - railway:track_ref - local_ref - ref - -# the track number tag in stop nodes, first match is taken, -# overwrites osm_edge_track_number_tags -osm_track_number_tags: - local_ref - ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 2 -routing_lvl4_fac: 2.5 -routing_lvl5_fac: 3.5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 7 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 3000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 100 - -routing_platform_unmatched_punish: 2000 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 100 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 100 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 1 - -# special line normalization for trains -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - ç -> c; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS with whitespace (ICE101 -> ICE 101) - ^([a-zA-Z]+)([0-9]+)$ -> \1 \2; - - # if a character line number is present, delete the numeric part - ^([a-zA-Z]+) [0-9]+$ -> \1; - -[bus] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - # highways - highway=motorway - highway=trunk - highway=primary - highway=secondary - highway=tertiary - highway=residential - highway=living_street - highway=unclassified - - # highway links - highway=motorway_link - highway=trunk_link - highway=primary_link - highway=secondary_link - highway=tertiary_link - highway=residential_link - - way=primary - way=seconday - way=bus_guideway - highway=bus_guideway - busway=* - psv=yes - psv=designated - - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - - route=bus - route=trolleybus - bus=yes - bus=designated - minibus=designated - minibus=yes - - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station|no_match_ways|no_match_rels - - # relations for the restriction system - type=restriction - type=restriction:bus - type=restriction:motorcar - -osm_filter_lvl1: - highway=secondary - highway=secondary_link - bus=yes - bus=designated - minibus=yes - minibus=designated - psv=designated - psv=yes - access=psv - access=bus - trolley_wire=yes - trolleywire=yes - trolleybus=yes - trolley_bus=yes - psv=designated - -osm_filter_lvl2: - highway=tertiary - highway=tertiary_link - -osm_filter_lvl3: - highway=unclassified - highway=residential - highway=road - -osm_filter_lvl4: - highway=living_street - highway=pedestrian - highway=service - psv=no - -osm_filter_lvl5: - bus=no - service=siding - access=permissive - access=private - access=no - service=parking_aisle - highway=footway - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - train=yes|no_match_ways - # access=no - public_transport=stop_area|no_match_nds|no_match_rels - type=multipolygon - railway=platform - railway=station - # service=parking_aisle - highway=proposed - highway=footway - highway=construction - building=yes - building=train_station - leisure=garden - leisure=park - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - -# Configuration of the OSM road restriction system -# We only support restriction with a single via node -# atm - -osm_node_negative_restriction: - restriction=no_right_turn - restriction=no_left_turn - restriction=no_u_turn - restriction=no_straight_on - restriction:bus=no_right_turn - restriction:bus=no_left_turn - restriction:bus=no_u_turn - restriction:bus=no_straight_on - -osm_node_positive_restriction: - restriction=only_left_turn - restriction=only_straight_on - restriction=only_right_turn - restriction:bus=only_left_turn - restriction:bus=only_straight_on - restriction:bus=only_right_turn - -osm_filter_no_restriction: - except=psv|mult_val_match - except=bus|mult_val_match - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - junction=roundabout # oneway=yes is implied - highway=motorway # oneway=yes is implied - oneway=yes - oneway=1 - oneway=true - oneway:bus=yes - oneway:bus=1 - oneway:bus=true - oneway:psv=yes - oneway:psv=1 - oneway:psv=true - -osm_filter_oneway_reverse: - oneway=-1 - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - oneway=false - oneway=0 - oneway=alternating - oneway=reversible - oneway=no - oneway:bus=no - oneway:bus=0 - oneway:bus=false - oneway:psv=no - oneway:psv=0 - oneway:psv=false - busway=opposite_lane - busway=opposite - busway:left=opposite_lane - busway:right=opposite_lane - psv=opposite_lane - psv=opposite - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - bus_stop=* - stop=* - highway=bus_stop - amenity=bus_station - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10 , 50, 100 - -osm_max_snap_level: 5 - -osm_max_osm_station_distance: 7.5 - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 3 -routing_lvl6_fac: 4 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 500 - -routing_station_distance_punish_fac: 2.5 - -routing_non_osm_station_punish: 500 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 20 - -# Max angle in a route from a station to an already reachable neighbor -routing_snap_full_turn_angle: 110 - -osm_max_node_block_distance: 10 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 4 - -routing_one_way_edge_punish: 5000 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -# routing_line_unmatched_punish_fac: 1.75 - -[tram, subway, funicular] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=tram - railway=subway - railway=light_rail - railway=tram - railway=funicular - railway=station - railway=halt - railway=tram_stop - route=subway - route=light_rail - subway=yes - tram=yes - -osm_filter_lv2: - service=siding - -osm_filter_lvl5: - service=crossover - service=yard - -# OSM entities to drop, as k=v. Applies to nodes, edges and -# relations. -# Nodes included in non-dropped ways are kept regardless of -# a matching drop filter. -# Ways included in non-dropped relations are kept regardless of -# a matching drop filter. - -osm_filter_drop: - area=yes - public_transport=stop_area - type=multipolygon - railway=platform - public_transport=platform - service=alley - -# Nodes that should act as "no-hup" nodes. These are nodes -# that are contained in multiple ways, but cannot be used -# to switch from one way to another (for example, a -# track crossing in rail networks) - -osm_filter_nohup: - railway:switch=no - railway=railway_crossing - -# Edges that should act as one-way nodes. - -osm_filter_oneway: - oneway=yes - -# Edges that may explicitely be used in -# both directions. May be used to set exception -# to "osm_filter_oneway" - -osm_filter_undirected: - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=subway - station=tram - railway=stop - railway=halt - railway=station - railway=tram_stop - railway=subway_stop - tram_stop=* - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 2000 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 235 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 80 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 80 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[ferry] - -# OSM entities to keep on different levels, as k=v. Applies -# to nodes, edges and relations. -# Nodes included in kept ways are always kept. -# Ways included in kept relations are always kept. - -osm_filter_keep: - route=ferry - waterway=river - motorboat=yes - ferry=yes - -# Nodes that are stations. -# Only nodes that have been kept during the filtering above will be -# checked. -osm_filter_station: - public_transport=stop_position - station=ferry - railway=stop - railway=halt - railway=station - stop=* - -# Relation fields that should be used for catching the lines that -# occur on an edge. Only relations that have been kept during the -# filtering above will be checked. The 'linename' will be normalized -# according to the rules in line_normalization_chain. -# The 'from_name' and 'to_name' will be normalized according to the -# rules in station_normalization_chain. -# The relations tags are given in the order of their relevance - -# the first normalized tag-value that is not null/empty will be -# taken. -osm_line_relation_tags: - line_name=ref,name # careful, no space after/before comma allowed! - from_name=from - to_name=to - - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 - -osm_max_snap_level: 4 - - -# sorted by priority, first found attr will be taken -osm_station_name_attrs: - name - uic_name - -# the track number tag in stop nodes, first one is taken -osm_track_number_tags: local_ref - -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 - -# Punishment (in meters) to add to the distance -# function if a vehicle performans a full turn -routing_full_turn_punish: 100 - -routing_station_distance_punish_fac: 3.14 - -routing_non_osm_station_punish: 50 - -# Max angle that should be counted as a full turn -routing_full_turn_angle: 45 - -# Max angle in a route from a station to an already reachable neighbar -routing_snap_full_turn_angle: 0 - -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -[tram, bus, subway, rail, gondola, funicular, ferry] -# Regular expressions and station comparision is -# always case insensitive! -station_normalize_chain: - , -> ' '; - - -> ' '; - — -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - \( -> ' '; - \) -> ' '; - \[ -> ' '; - \] -> ' '; - / -> ' '; - '\\' -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - ô -> o; - ç -> c; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - str\. -> strasse; - av\. -> avenue; - - # always separate 'street', 'strasse' - '([a-zA-Z])strasse($| )' -> '\1 strasse\2'; - '([a-zA-Z])street($| )' -> '\1 street\2'; - - # always use "street" - '(^| )strasse($| )' -> '\1street\2'; - - # always use "avenue" - '(^| )avenida($| )' -> '\1avenue\2'; - '(^| )avenu($| )' -> '\1avenue\2'; - - # normalize every possible abbr. of german "Bahnhof", "Hauptbahnhof", "Busbahnhof" - '(^| )hauptbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hauptbhf($| )' -> '\1hauptbahnhof\2'; - '(^| )zentraler busbahnhof($| )$' -> \1busbahnhof\2; - '(^| )zentraler omnibusbahnhof($| )$' -> \1busbahnhof\2; - '(^| )omnibusbahnhof($| )' -> '\1busbahnhof\2'; - '(^| )omnibusbhf($| )' -> '\1busbahnhof\2'; - '(^| )busbf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbf($| )' -> '\1busbahnhof\2'; - '(^| )bus bf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bf($| )' -> '\1busbahnhof\2'; - '(^| )busbhf\.($| )' -> '\1busbahnhof\2'; - '(^| )busbhf($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf\.($| )' -> '\1busbahnhof\2'; - '(^| )bus bhf($| )' -> '\1busbahnhof\2'; - '(^| )zob($| )' -> '\1busbahnhof\2'; - '(^| )hbf\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hbf($| )' -> '\1hauptbahnhof\2'; - '(^| )hb\.($| )' -> '\1hauptbahnhof\2'; - '(^| )hb($| )' -> '\1hauptbahnhof\2'; - '(^| )bf\.($| )' -> '\1bahnhof\2'; - '(^| )bf($| )' -> '\1bahnhof\2'; - '(^| )bhf\.($| )' -> '\1bahnhof\2'; - '(^| )bhf($| )' -> '\1bahnhof\2'; - '(^| )bhfeingang($| )' -> '\1bahnhofeingang\2'; - '(^| )gare de($| )' -> '\1gare\2'; - - - # if a stations starts with single station identifier - # always put it at the end (for example, "hauptbahnhof freiburg" becomes "freiburg hauptbahnhof") - '^hauptbahnhof (.+)$' -> \1 hauptbahnhof; - '^bahnhof (.+)$' -> \1 bahnhof; - '^busbahnhof (.+)$' -> \1 busbahnhof; - '^gare (.+)$' -> \1 gare; - '^station (.+)$' -> \1 station; - - '(^| )busbahnhof($| )' -> '\1bbahnhof\2'; - - # normalize line types in station names - '(^| )u bahn\.($| )' -> '\1ubahn\2'; - '(^| )metro\.($| )' -> '\1ubahn\2'; - '(^| )subway\.($| )' -> '\1ubahn\2'; - '(^| )underground\.($| )' -> '\1ubahn\2'; - '(^| )ubahn($| )' -> '\1u\2'; - '(^| )s bahn\.($| )' -> '\1sbahn\2'; - '(^| )sbahn($| )' -> '\1s\2'; - '(^| )tramway($| )' -> '\1tram\2'; - '(^| )stadtbahn($| )' -> '\1tram\2'; - '(^| )strassenbahn($| )' -> '\1tram\2'; - '(^| )streetcar($| )' -> '\1tram\2'; - '(^| )tram($| )' -> '\1t\2'; - - # delete track information from name - '(^| )kante [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )gleis [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )track [a-zA-Z0-9]{1,2}($| )' -> ' '; - '(^| )voie [a-zA-Z0-9]{1,2}($| )' -> ' '; - - # abbrv - '(^| )und($| )' -> '\1+\2'; - '(^| )and($| )' -> '\1+\2'; - '(^| )et($| )' -> '\1+\2'; - - # noise - '\sde\s' -> ' '; - '\sda\s' -> ' '; - '\sdi\s' -> ' '; - '\sdel\s' -> ' '; - '\sdal\s' -> ' '; - - # abbrv in most western languages - '(^| )saint ' -> '\1st. '; - '(^| )sankt ' -> '\1st. '; - '(^| )sanct ' -> '\1st. '; - - \. -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - -line_normalize_chain: - , -> ' '; - - -> ' '; - _ -> ' '; - " -> ''; - ' -> ''; - ` -> ''; - / -> ' '; - < -> ' '; - > -> ' '; - & -> '+'; - ä -> ae; - ö -> oe; - ü -> ue; - ß -> ss; - è -> e; - é -> e; - á -> a; - à -> a; - ó -> o; - ò -> o; - í -> i; - ú -> u; - ù -> u; - ë -> e; - å -> ae; - ç -> c; - â -> a; - ê -> e; - ï -> i; - œ -> oe; - ø -> oe; - ^line -> ''; - ^linie -> ''; - ^metro -> ''; - ^tram -> ''; - ^strassenbahn -> ''; - ^bus -> ''; - - # delete everything in brackets - \(.+\) -> ' '; - \[.+\] -> ' '; - - # whitespace - \s+ -> ' '; - ^\s -> ''; - \s$ -> ''; - - # line/number combs ALWAYS without whitespace (T 2 -> T2) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - -track_normalize_chain: - '(^| )gleis($| )' -> ''; - '(^| )gl\.($| )' -> ''; - '(^| )platform($| )' -> ''; - '(^| )track($| )' -> ''; - '(^| )rail($| )' -> ''; - # line/number combs ALWAYS without whitespace (1 A -> 1A) - ^([a-zA-Z]+) ([0-9]+)$ -> \1\2; - ^([0-9]+) ([a-zA-Z]+)$ -> \1\2; - - # delete track numbers greater than 999 - ^[0-9]{4,}$ -> ''; diff --git a/geo/pfaedle.qgs b/geo/pfaedle.qgs index 2ec03bf..aa197d3 100644 --- a/geo/pfaedle.qgs +++ b/geo/pfaedle.qgs @@ -1,176 +1,140 @@ - - + - - - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo-Mercator - merc - WGS84 - false - - - + - + - + - + - + - + - + - + - - + + - + - + - + - + - - - - - - - + - + - - OGRGeoJSON_Point20180203134333739 - OGRGeoJSON_LineString20180203134333975 - OGRGeoJSON_Point20180206114956218 - OGRGeoJSON_LineString20180206114956229 - path20180217155708341 - trgraph_trgraph_LineString20180508200527144 - trgraph_trgraph_Point20180508200527256 - OSM_Transportation20181215024818603 - OpenStreetMap_de20181215024846026 - - - - - - - - - - - - - + meters - -374853.74009754881262779 - 4605645.85081499628722668 - 2640376.65992686524987221 - 7553306.65061968378722668 + 866081.24618305882904679 + 6076552.62015097495168447 + 870662.44395622855518013 + 6080842.96235341485589743 0 + 1 - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - 3857 - 3857 - EPSG:3857 - WGS 84 / Pseudo-Mercator + +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs + 1353 + 3395 + EPSG:3395 + WGS 84 / World Mercator merc WGS84 false 0 + + + + + + + - + + + path20180217155708341 + OGRGeoJSON_Point20180203134333739 + OGRGeoJSON_LineString20180203134333975 + OGRGeoJSON_Point20180206114956218 + OGRGeoJSON_LineString20180206114956229 + OSM_Transportation20181215024818603 + OpenStreetMap_de20181215024846026 + + - + - - - - - - - - + - - + 6.70734330570000026 47.04982400000000098 @@ -195,1730 +159,1127 @@ true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + 0 0 - 1 - - - - + 0 + station_alt_names + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -1941,55 +1302,35 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + 6.70734330570000026 47.05522500000000008 @@ -2014,480 +1355,129 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - + + + + + ogr - - - - - 1 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -2510,53 +1500,35 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + 6.70734330570000026 47.04982400000000098 @@ -2581,615 +1553,326 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + - + 0 0 - 1 - - - - + 0 + station_alt_names + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -3212,59 +1895,39 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + 6.70734330570000026 47.05522500000000008 @@ -3289,432 +1952,208 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + + - + 0 0 - 1 - - - - + 0 + station_alt_names + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - . + . 0 . @@ -3737,53 +2176,34 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + station_name - - + -20037508.34278924390673637 -20037508.34278925508260727 @@ -3803,70 +2223,33 @@ def my_form_open(dialog, layer, feature): 3857 3857 EPSG:3857 - WGS 84 / Pseudo-Mercator + WGS 84 / Pseudo Mercator merc WGS84 false - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - + + + wms - + - - 1 - 1 - 1 - - - - - + - - None - WholeRaster - Estimated - 0.02 - 0.98 - 2 - - + 0 - + -20037508.34278924390673637 -20037508.34278925508260727 @@ -3886,70 +2269,33 @@ def my_form_open(dialog, layer, feature): 3857 3857 EPSG:3857 - WGS 84 / Pseudo-Mercator + WGS 84 / Pseudo Mercator merc WGS84 false - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - + + + wms - + - - 1 - 1 - 1 - - - - - + - - None - WholeRaster - Estimated - 0.02 - 0.98 - 2 - - + 0 - + 6.70738836720000009 47.05522500000000008 @@ -3974,193 +2320,147 @@ def my_form_open(dialog, layer, feature): true - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - ogr - - - - - 1 - 1 - 1 - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + + - + 0 0 - 0.7 - - - - + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + - - - - - - + + - + - - - - - - . + . 0 . @@ -4183,1965 +2483,96 @@ def my_form_open(dialog, layer, feature): ]]> 0 generatedlayout - - - - ver - - - trgraph_trgraph_LineString20180508200527144 - ./trgraph.json|layerid=0|geometrytype=LineString - - - - trgraph trgraph LineString - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - - ogr - - - - - - - - - - - 1 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . - - 0 - . - - 0 - generatedlayout - - - - - id - - - trgraph_trgraph_Point20180508200527256 - ./trgraph.json|layerid=0|geometrytype=Point - - - - trgraph trgraph Point - - - +proj=longlat +datum=WGS84 +no_defs - 3452 - 4326 - EPSG:4326 - WGS 84 - longlat - WGS84 - true - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - false - - - - - - - - - - - - - ogr - - - - - - - - - - - 1 - 1 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . - - 0 - . - - 0 - generatedlayout - - - - id - - - - - - - - - - - - - 255 - - - - - true - - - - - - +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs - EPSG:3857 - 3857 - 1 - - - meters - m2 - - - - - - false - - - - - - - - - - 30 - false - true - 0 - false - 0 - 16 - 50 - false - - false - - - 2 - D - true - - - - - - conditions unknown - - false - false - - 255 - 255 - 240 - 0 - 240 - 255 - 240 - - 90 + - + false - - - - 2 - 2 - 2 - 2 - 2 - 2 - 2 - - off - - OGRGeoJSON_LineString20180203134333975 - OGRGeoJSON_LineString20180206114956229 - OGRGeoJSON_Point20180203134333739 - OGRGeoJSON_Point20180206114956218 - path20180217155708341 - trgraph_trgraph_LineString20180508200527144 - trgraph_trgraph_Point20180508200527256 - - - - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - 0.000000 - - 0 - 2 - - to_vertex - to_vertex - to_vertex - to_vertex - to_vertex - to_vertex_and_segment - to_vertex_and_segment - - - enabled - enabled - enabled - enabled - enabled - disabled - disabled - - current_layer - + + + + + + false + + + + + + 2 + true + MU + + + false + + + + false WGS84 - None + 8 + false + + + + + + + 0 + 255 + 255 + 255 + 255 + 255 + 255 + + + + - false - - 8 + None + false + + +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs + EPSG:3395 + 1353 + 1 + + + + + + true + 255 + + + conditions unknown + 90 + + meters + m2 + + - - - - - - - - - - - - - - diff --git a/pfaedle.cfg b/pfaedle.cfg index 8840534..1ec3a62 100644 --- a/pfaedle.cfg +++ b/pfaedle.cfg @@ -3,6 +3,10 @@ # Authors: Patrick Brosi [tram, bus, coach, subway, rail, gondola, funicular, ferry] + +routing_transition_penalty_fac: 0.0083 +routing_station_move_penalty_fac: 0.00087 + # Regular expressions and station comparision is # always case insensitive! station_normalize_chain: @@ -334,22 +338,9 @@ osm_line_relation_tags: from_name=from to_name=to -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - [public_transport=stop_area]uic_ref=500 - [public_transport=stop_area]wikidata=500 - name=100 - [public_transport=stop_area]name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 200 # max edge level to which station will be snapped osm_max_snap_level: 2 @@ -372,24 +363,31 @@ osm_track_number_tags: local_ref ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 2 -routing_lvl4_fac: 2.5 -routing_lvl5_fac: 3.5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 7 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 120 # default level +osm_lvl1_avg_speed: 90 +osm_lvl2_avg_speed: 65 +osm_lvl3_avg_speed: 50 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 3000 +routing_full_turn_penalty: 180 # 3 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 100 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 -routing_platform_unmatched_punish: 2000 +# If the platform does not match, add this penalty +routing_platform_unmatched_penalty: 0.1 + +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 100 @@ -397,24 +395,25 @@ routing_full_turn_angle: 100 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 100 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 5 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# Additional one-time time penalty for entering a one-way segment +# in seconds +osm_one_way_entry_cost: 300 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.5 +routing_line_station_to_unmatched_time_penalty: 1.2 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 # special line normalization for trains line_normalize_chain: @@ -558,6 +557,12 @@ osm_filter_keep: type=restriction:motorcar osm_filter_lvl1: + highway=trunk + highway=trunk_link + highway=primary + highway=primary_link + +osm_filter_lvl2: highway=secondary highway=secondary_link bus=yes @@ -574,22 +579,22 @@ osm_filter_lvl1: trolley_bus=yes psv=designated -osm_filter_lvl2: +osm_filter_lvl3: highway=tertiary highway=tertiary_link -osm_filter_lvl3: +osm_filter_lvl4: highway=unclassified highway=residential highway=road -osm_filter_lvl4: +osm_filter_lvl5: highway=living_street highway=pedestrian highway=service psv=no -osm_filter_lvl5: +osm_filter_lvl6: bus=no service=siding access=permissive @@ -597,6 +602,7 @@ osm_filter_lvl5: access=no service=parking_aisle highway=footway + highway=track # OSM entities to drop, as k=v. Applies to nodes, edges and # relations. @@ -714,6 +720,12 @@ osm_filter_station: highway=bus_stop amenity=bus_station +osm_filter_turning_cycle: + highway=turning_cycle + highway=turning_loop + junction=roundabout + highway=mini_roundabout + # Relation fields that should be used for catching the lines that # occur on an edge. Only relations that have been kept during the # filtering above will be checked. The 'linename' will be normalized @@ -727,26 +739,20 @@ osm_line_relation_tags: line_name=ref,name # careful, no space after/before comma allowed! from_name=from to_name=to + line_color=colour,color +# max distance in meters between a OSM station candidate +# and the input GTFS station +osm_max_station_cand_distance: 200 -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 - -osm_max_snap_fallback_distance: 300 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 5 +# max distance between a snapped OSM station and is snap +# point to still be considered a "OSM station" osm_max_osm_station_distance: 8.0 # sorted by priority, first found attr will be taken @@ -757,22 +763,48 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.25 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 3 -routing_lvl6_fac: 4 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 5 + +# Additional one-time time penalty for entering a one-way segment +# in seconds +osm_one_way_entry_cost: 300 + +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.5 +routing_line_station_to_unmatched_time_penalty: 1.2 +routing_line_station_from_unmatched_time_penalty: 1.1 + +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 + +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.1 + +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 500 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 2.5 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 500 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 20 @@ -782,26 +814,6 @@ routing_snap_full_turn_angle: 110 osm_max_node_block_distance: 10 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 - -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 4 - -routing_one_way_edge_punish: 5000 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -# routing_line_unmatched_punish_fac: 1.75 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 [coach] @@ -850,14 +862,14 @@ osm_filter_lvl7: service=parking_aisle highway=footway -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.15 -routing_lvl2_fac: 1.5 -routing_lvl3_fac: 1.75 -routing_lvl4_fac: 2.25 -routing_lvl5_fac: 2.5 -routing_lvl6_fac: 3 -routing_lvl7_fac: 4 +osm_lvl0_avg_speed: 120 # default level +osm_lvl1_avg_speed: 90 +osm_lvl2_avg_speed: 65 +osm_lvl3_avg_speed: 50 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 osm_max_snap_level: 5 @@ -958,20 +970,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 4 @@ -984,22 +985,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 2000 +routing_full_turn_penalty: 180 # 3 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 235 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 80 @@ -1007,24 +1011,21 @@ routing_full_turn_angle: 80 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 80 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.5 +routing_line_station_to_unmatched_time_penalty: 1.2 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 [gondola] @@ -1104,20 +1105,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 4 @@ -1130,22 +1120,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 2000 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 235 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 80 @@ -1153,24 +1146,21 @@ routing_full_turn_angle: 80 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 80 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.2 +routing_line_station_to_unmatched_time_penalty: 1.15 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 [funicular] @@ -1281,20 +1271,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 50, 100 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 100 osm_max_snap_level: 4 @@ -1307,22 +1286,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 85 # default level +osm_lvl1_avg_speed: 70 +osm_lvl2_avg_speed: 55 +osm_lvl3_avg_speed: 40 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 20 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 2000 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 235 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 80 @@ -1330,24 +1312,21 @@ routing_full_turn_angle: 80 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 80 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 100 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.2 +routing_line_station_to_unmatched_time_penalty: 1.15 +routing_line_station_from_unmatched_time_penalty: 1.1 -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 [ferry] @@ -1366,7 +1345,10 @@ osm_filter_keep: # Only nodes that have been kept during the filtering above will be # checked. osm_filter_station: + ferry=yes public_transport=stop_position + amenity=ferry_terminal + mooring=ferry station=ferry railway=stop railway=halt @@ -1387,20 +1369,9 @@ osm_line_relation_tags: from_name=from to_name=to - -# attr name together with the -# max distance in meters between any of the groups members and -# a potential new member -# first matching rule will be taken -# only applies to nodes that match osm_filter_station! -osm_station_group_attrs: - uic_ref=500 - wikidata=500 - name=100 - -# max distance in meters between a snapped station position and the -# original station position -osm_max_snap_distance: 10, 100, 200 +# max distance in meters between a snapped position on an +# edge and the input GTFS/OSM station +osm_max_snap_distance: 500 osm_max_snap_level: 4 @@ -1413,22 +1384,25 @@ osm_station_name_attrs: # the track number tag in stop nodes, first one is taken osm_track_number_tags: local_ref -routing_lvl0_fac: 1 # default level -routing_lvl1_fac: 1.5 -routing_lvl2_fac: 2 -routing_lvl3_fac: 2.5 -routing_lvl4_fac: 3.5 -routing_lvl5_fac: 5 -routing_lvl6_fac: 5 -routing_lvl7_fac: 5 +# avg speed on segment levels, in km/h +osm_lvl0_avg_speed: 70 # default level +osm_lvl1_avg_speed: 60 +osm_lvl2_avg_speed: 50 +osm_lvl3_avg_speed: 35 +osm_lvl4_avg_speed: 30 +osm_lvl5_avg_speed: 25 +osm_lvl6_avg_speed: 10 +osm_lvl7_avg_speed: 5 -# Punishment (in meters) to add to the distance +# Punishment (in seconds) to add to the distance # function if a vehicle performans a full turn -routing_full_turn_punish: 100 +routing_full_turn_penalty: 120 # 2 minutes -routing_station_distance_punish_fac: 3.14 +# Penalty added to non-station placements +routing_non_station_penalty: 0.5 -routing_non_osm_station_punish: 50 +# If the station name does not match, add this penalty +routing_station_unmatched_penalty: 0.3 # Max angle that should be counted as a full turn routing_full_turn_angle: 45 @@ -1436,22 +1410,18 @@ routing_full_turn_angle: 45 # Max angle in a route from a station to an already reachable neighbar routing_snap_full_turn_angle: 0 -# Punishment (in meters) to add to the distance -# function if a vehicle passes a station node without -# stopping there -routing_pass_thru_station_punish: 0 +# Factor by which the vehicle slows down in a one way street (factor 5 +# means it will take 5 times longer) +osm_one_way_speed_penalty_fac: 2 -# Punishment factor for every meter a vehicle -# travels through a one-way edge -routing_one_way_meter_punish_fac: 1 - -# Punishment factor for every meter a vehicle -# travels through an edge without any matching line -# information -routing_line_unmatched_punish_fac: 0.5 - -# Punishment factor for every meter a vehicle -# travels through an edge without any line -# information when no specific line was requested -# routing_no_lines_punish_fac: 0 +# If a segment has no matching line attributes, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +routing_line_unmatched_time_penalty_fac: 1.2 +routing_line_station_to_unmatched_time_penalty: 1.15 +routing_line_station_from_unmatched_time_penalty: 1.1 +# If a segment has no line attributes at all, multiply the +# time needed to traverse it with the given factor (should +# be > 1 for a punishment, values < 1 will prefer unmatching segments) +# routing_no_lines_penalty_fac: 1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7d54350..fbf3449 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,3 +12,4 @@ add_subdirectory(util) add_subdirectory(pfaedle) add_subdirectory(cppgtfs) add_subdirectory(configparser) +add_subdirectory(shapevl) diff --git a/src/configparser b/src/configparser index b2d0c99..ca166b3 160000 --- a/src/configparser +++ b/src/configparser @@ -1 +1 @@ -Subproject commit b2d0c99b9c84f62f5f7b259524e0f4b1c9d38318 +Subproject commit ca166b3446d5bb8b5fb8c6f637ca3f9cb0a8ff3b diff --git a/src/cppgtfs b/src/cppgtfs index be682b2..6328d02 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit be682b2cc13125147bad9ebe544f0bad25d0bd22 +Subproject commit 6328d026f6fa870c87b18ff93ab6f4b65a869686 diff --git a/src/pfaedle/CMakeLists.txt b/src/pfaedle/CMakeLists.txt index 22b083a..8adb8d6 100644 --- a/src/pfaedle/CMakeLists.txt +++ b/src/pfaedle/CMakeLists.txt @@ -18,3 +18,5 @@ add_library(pfaedle_dep ${pfaedle_SRC}) include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src) target_link_libraries(pfaedle pfaedle_dep util configparser ad_cppgtfs -lpthread) + +add_subdirectory(tests) diff --git a/src/pfaedle/Def.h b/src/pfaedle/Def.h index 7b8e083..9308161 100644 --- a/src/pfaedle/Def.h +++ b/src/pfaedle/Def.h @@ -17,17 +17,17 @@ #define __str_c(s) s ## 1 #define __str_d(s) __str_c(s) -#if !defined(PFAEDLE_PRECISION) || (__str_d(PFAEDLE_PRECISION) == 1) -#undef PFAEDLE_PRECISION -#define PFAEDLE_PRECISION double +#if !defined(PFDL_PREC) || (__str_d(PFDL_PREC) == 1) +#undef PFDL_PREC +#define PFDL_PREC double #endif -#define PFAEDLE_PRECISION_STR __str_a(PFAEDLE_PRECISION) +#define PFDL_PREC_STR __str_a(PFDL_PREC) -#define POINT util::geo::Point -#define LINE util::geo::Line -#define BOX util::geo::Box -#define POLYLINE util::geo::PolyLine +#define POINT util::geo::Point +#define LINE util::geo::Line +#define BOX util::geo::Box +#define POLYLINE util::geo::PolyLine #define BOX_PADDING 2500 diff --git a/src/pfaedle/PfaedleMain.cpp b/src/pfaedle/PfaedleMain.cpp index 55bea77..4082f16 100644 --- a/src/pfaedle/PfaedleMain.cpp +++ b/src/pfaedle/PfaedleMain.cpp @@ -18,19 +18,19 @@ #include "pfaedle/config/ConfigReader.h" #include "pfaedle/config/MotConfig.h" #include "pfaedle/config/MotConfigReader.h" -#include "pfaedle/eval/Collector.h" #include "pfaedle/gtfs/Feed.h" #include "pfaedle/gtfs/Writer.h" #include "pfaedle/netgraph/Graph.h" #include "pfaedle/osm/OsmIdSet.h" #include "pfaedle/router/ShapeBuilder.h" +#include "pfaedle/router/Stats.h" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "pfaedle/trgraph/Graph.h" -#include "pfaedle/trgraph/StatGroup.h" +#include "util/Misc.h" #include "util/geo/output/GeoGraphJsonOutput.h" #include "util/geo/output/GeoJsonOutput.h" #include "util/json/Writer.h" #include "util/log/Log.h" -#include "util/Misc.h" #ifndef CFG_HOME_SUFFIX #define CFG_HOME_SUFFIX "/.config" @@ -42,16 +42,25 @@ #define CFG_FILE_NAME "pfaedle.cfg" #endif -using pfaedle::router::MOTs; +using configparser::ParseFileExc; +using pfaedle::config::Config; +using pfaedle::config::ConfigReader; +using pfaedle::config::MotConfig; +using pfaedle::config::MotConfigReader; using pfaedle::osm::BBoxIdx; using pfaedle::osm::OsmBuilder; -using pfaedle::config::MotConfig; -using pfaedle::config::Config; +using pfaedle::router::DistDiffTransWeight; +using pfaedle::router::DistDiffTransWeightNoHeur; +using pfaedle::router::ExpoTransWeight; +using pfaedle::router::ExpoTransWeightNoHeur; +using pfaedle::router::MOTs; +using pfaedle::router::NormDistrTransWeight; +using pfaedle::router::NormDistrTransWeightNoHeur; +using pfaedle::router::Router; +using pfaedle::router::RouterImpl; using pfaedle::router::ShapeBuilder; -using configparser::ParseFileExc; -using pfaedle::config::MotConfigReader; -using pfaedle::config::ConfigReader; -using pfaedle::eval::Collector; +using pfaedle::router::Stats; +using pfaedle::statsimiclassifier::JaccardClassifier; enum class RetCode { SUCCESS = 0, @@ -77,6 +86,11 @@ int main(int argc, char** argv) { // initialize randomness srand(time(NULL) + rand()); // NOLINT + // use utf8 locale + std::setlocale(LC_ALL, "en_US.utf8"); + + T_START(total); + Config cfg; MotConfigReader motCfgReader; @@ -84,13 +98,11 @@ int main(int argc, char** argv) { cr.read(&cfg, argc, argv); std::vector gtfs(cfg.feedPaths.size()); - // feed containing the shapes in memory for evaluation - ad::cppgtfs::gtfs::Feed evalFeed; std::vector cfgPaths = getCfgPaths(cfg); try { - motCfgReader.parse(cfgPaths); + motCfgReader.parse(cfgPaths, cfg.motCfgParam); } catch (const configparser::ParseExc& ex) { LOG(ERROR) << "Could not parse MOT configurations, reason was:"; std::cerr << ex.what() << std::endl; @@ -108,27 +120,24 @@ int main(int argc, char** argv) { exit(static_cast(RetCode::NO_MOT_CFG)); } + T_START(gtfsBuild); + if (cfg.feedPaths.size() == 1) { if (cfg.inPlace) cfg.outputPath = cfg.feedPaths[0]; if (!cfg.writeOverpass) - LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ..."; + LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[0] << " ..."; try { ad::cppgtfs::Parser p; p.parse(>fs[0], cfg.feedPaths[0]); - if (cfg.evaluate) { - // read the shapes and store them in memory - p.parseShapes(&evalFeed, cfg.feedPaths[0]); - } } catch (const ad::cppgtfs::ParserException& ex) { LOG(ERROR) << "Could not parse input GTFS feed, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_PARSE_ERR)); } - if (!cfg.writeOverpass) LOG(INFO) << "Done."; } else if (cfg.writeOsm.size() || cfg.writeOverpass) { for (size_t i = 0; i < cfg.feedPaths.size(); i++) { if (!cfg.writeOverpass) - LOG(INFO) << "Reading " << cfg.feedPaths[i] << " ..."; + LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[i] << " ..."; ad::cppgtfs::Parser p; try { p.parse(>fs[i], cfg.feedPaths[i]); @@ -137,13 +146,14 @@ int main(int argc, char** argv) { std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_PARSE_ERR)); } - if (!cfg.writeOverpass) LOG(INFO) << "Done."; } } else if (cfg.feedPaths.size() > 1) { std::cerr << "Multiple feeds only allowed in filter mode." << std::endl; exit(static_cast(RetCode::MULT_FEEDS_NOT_ALWD)); } + auto tGtfsBuild = T_STOP(gtfsBuild); + LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size() << " unique MOT configs."; MOTs cmdCfgMots = cfg.mots; @@ -161,12 +171,20 @@ int main(int argc, char** argv) { } } + double maxSpeed = 0; + for (const auto& c : motCfgReader.getConfigs()) { + if (c.osmBuildOpts.maxSpeed > maxSpeed) { + maxSpeed = c.osmBuildOpts.maxSpeed; + } + } + if (cfg.writeOsm.size()) { LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ..."; BBoxIdx box(BOX_PADDING); + for (size_t i = 0; i < cfg.feedPaths.size(); i++) { ShapeBuilder::getGtfsBox(>fs[i], cmdCfgMots, cfg.shapeTripId, true, - &box); + &box, maxSpeed); } OsmBuilder osmBuilder; std::vector opts; @@ -188,7 +206,7 @@ int main(int argc, char** argv) { BBoxIdx box(BOX_PADDING); for (size_t i = 0; i < cfg.feedPaths.size(); i++) { ShapeBuilder::getGtfsBox(>fs[i], cmdCfgMots, cfg.shapeTripId, true, - &box); + &box, maxSpeed); } OsmBuilder osmBuilder; std::vector opts; @@ -205,11 +223,6 @@ int main(int argc, char** argv) { exit(static_cast(RetCode::NO_INPUT_FEED)); } - std::vector dfBins; - auto dfBinStrings = util::split(std::string(cfg.evalDfBins), ','); - for (auto st : dfBinStrings) dfBins.push_back(atof(st.c_str())); - Collector ecoll(cfg.evalPath, dfBins); - for (const auto& motCfg : motCfgReader.getConfigs()) { std::string filePost; auto usedMots = pfaedle::router::motISect(motCfg.mots, cmdCfgMots); @@ -220,7 +233,7 @@ int main(int argc, char** argv) { filePost = getFileNameMotStr(usedMots); std::string motStr = pfaedle::router::getMotStr(usedMots); - LOG(INFO) << "Calculating shapes for mots " << motStr; + LOG(INFO) << "Matching shapes for mots " << motStr; try { pfaedle::router::FeedStops fStops = @@ -231,62 +244,117 @@ int main(int argc, char** argv) { pfaedle::osm::OsmBuilder osmBuilder; pfaedle::osm::BBoxIdx box(BOX_PADDING); - ShapeBuilder::getGtfsBox(>fs[0], cmdCfgMots, cfg.shapeTripId, - cfg.dropShapes, &box); + ShapeBuilder::getGtfsBox(>fs[0], usedMots, cfg.shapeTripId, + cfg.dropShapes, &box, + motCfg.osmBuildOpts.maxSpeed); + + T_START(osmBuild); if (fStops.size()) osmBuilder.read(cfg.osmPath, motCfg.osmBuildOpts, &graph, box, - cfg.gridSize, &fStops, &restr); + cfg.gridSize, &restr); - // TODO(patrick): move this somewhere else - for (auto& feedStop : fStops) { - if (feedStop.second) { - feedStop.second->pl().getSI()->getGroup()->writePens( - motCfg.osmBuildOpts.trackNormzer, - motCfg.routingOpts.platformUnmatchedPen, - motCfg.routingOpts.stationDistPenFactor, - motCfg.routingOpts.nonOsmPen); - } + auto tOsmBuild = T_STOP(osmBuild); + + JaccardClassifier statsimiClassifier; + + Router* router = 0; + + if (motCfg.routingOpts.transPenMethod == "exp") { + if (cfg.noAStar) + router = new RouterImpl(); + else + router = new RouterImpl(); + } else if (motCfg.routingOpts.transPenMethod == "distdiff") { + if (cfg.noAStar) + router = new RouterImpl(); + else + router = new RouterImpl(); + } else if (motCfg.routingOpts.transPenMethod == "timenorm") { + if (cfg.noAStar) + router = new RouterImpl(); + else + router = new RouterImpl(); + } else { + LOG(ERROR) << "Unknown routing method " + << motCfg.routingOpts.transPenMethod; + exit(1); } - ShapeBuilder shapeBuilder(>fs[0], &evalFeed, cmdCfgMots, motCfg, &ecoll, - &graph, &fStops, &restr, cfg); + ShapeBuilder shapeBuilder(>fs[0], usedMots, motCfg, &graph, &fStops, + &restr, &statsimiClassifier, router, cfg); + + pfaedle::netgraph::Graph ng; + Stats stats; + + if (singleTrip) { + mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + std::ofstream pstr(cfg.dbgOutputPath + "/path.json"); + util::geo::output::GeoJsonOutput o(pstr); + + auto l = shapeBuilder.shapeL(singleTrip); + stats = l.second; + + LOG(INFO) << "Outputting path.json..."; + // reproject to WGS84 to match RFC 7946 + o.print(l.first, {}); + + o.flush(); + pstr.close(); + } else { + stats = shapeBuilder.shapeify(&ng); + } + + // outputting stats + + if (cfg.writeStats) { + size_t numEdgs = 0; + for (const auto& nd : graph.getNds()) { + numEdgs += nd->getAdjListOut().size(); + } + util::json::Dict jsonStats = { + {"statistics", + util::json::Dict{ + {"gtfs_num_stations", gtfs[0].getStops().size()}, + {"gtfs_num_trips", gtfs[0].getTrips().size()}, + {"graph_nds", graph.getNds().size()}, + {"graph_edgs", numEdgs}, + {"num_tries", stats.numTries}, + {"num_trie_leafs", stats.numTrieLeafs}, + {"dijkstra_iters", stats.dijkstraIters}, + {"time_solve", stats.solveTime}, + {"time_read_osm", tOsmBuild}, + {"time_read_gtfs", tGtfsBuild}, + {"time_tot", T_STOP(total)}, + {"peak-memory", util::readableSize(util::getPeakRSS())}, + {"peak-memory-bytes", util::getPeakRSS()}}}}; + + std::ofstream ofs; + ofs.open("stats" + filePost + ".json"); + util::json::Writer wr(&ofs, 10, true); + wr.val(jsonStats); + wr.closeAll(); + } + + if (router) delete router; if (cfg.writeGraph) { LOG(INFO) << "Outputting graph.json..."; util::geo::output::GeoGraphJsonOutput out; mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); std::ofstream fstr(cfg.dbgOutputPath + "/graph.json"); - out.printLatLng(*shapeBuilder.getGraph(), fstr); + out.print(*shapeBuilder.getGraph(), fstr); fstr.close(); } - if (singleTrip) { - LOG(INFO) << "Outputting path.json..."; - mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - std::ofstream pstr(cfg.dbgOutputPath + "/path.json"); - util::geo::output::GeoJsonOutput o(pstr); - - auto l = shapeBuilder.shapeL(singleTrip); - - // reproject to WGS84 to match RFC 7946 - o.printLatLng(l, {}); - - o.flush(); - pstr.close(); - - exit(static_cast(RetCode::SUCCESS)); - } - - pfaedle::netgraph::Graph ng; - shapeBuilder.shape(&ng); + if (singleTrip) exit(static_cast(RetCode::SUCCESS)); if (cfg.buildTransitGraph) { util::geo::output::GeoGraphJsonOutput out; LOG(INFO) << "Outputting trgraph" + filePost + ".json..."; mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); std::ofstream fstr(cfg.dbgOutputPath + "/trgraph" + filePost + ".json"); - out.printLatLng(ng, fstr); + out.print(ng, fstr); fstr.close(); } } catch (const pfxml::parse_exc& ex) { @@ -296,8 +364,6 @@ int main(int argc, char** argv) { } } - if (cfg.evaluate) ecoll.printStats(&std::cout); - if (cfg.feedPaths.size()) { try { mkdir(cfg.outputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); @@ -305,7 +371,7 @@ int main(int argc, char** argv) { pfaedle::gtfs::Writer w; w.write(>fs[0], cfg.outputPath); } catch (const ad::cppgtfs::WriterException& ex) { - LOG(ERROR) << "Could not write final GTFS feed, reason was:"; + LOG(ERROR) << "Could not write output GTFS feed, reason was:"; std::cerr << ex.what() << std::endl; exit(static_cast(RetCode::GTFS_WRITE_ERR)); } @@ -329,12 +395,10 @@ std::vector getCfgPaths(const Config& cfg) { if (cfg.configPaths.size()) return cfg.configPaths; std::vector ret; - // install prefix global configuration path, if available { - auto path = std::string(INSTALL_PREFIX) + - std::string(CFG_DIR) + "/" + "pfaedle" + "/" + - CFG_FILE_NAME; + auto path = std::string(INSTALL_PREFIX) + std::string(CFG_DIR) + "/" + + "pfaedle" + "/" + CFG_FILE_NAME; std::ifstream is(path); LOG(DEBUG) << "Testing for config file at " << path; @@ -346,8 +410,8 @@ std::vector getCfgPaths(const Config& cfg) { // local user configuration path, if available { - auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + - "pfaedle" + "/" + CFG_FILE_NAME; + auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + "pfaedle" + "/" + + CFG_FILE_NAME; std::ifstream is(path); LOG(DEBUG) << "Testing for config file at " << path; diff --git a/src/pfaedle/config/ConfigReader.cpp b/src/pfaedle/config/ConfigReader.cpp index c9ef614..1d351e6 100644 --- a/src/pfaedle/config/ConfigReader.cpp +++ b/src/pfaedle/config/ConfigReader.cpp @@ -11,6 +11,7 @@ #include "pfaedle/_config.h" #include "pfaedle/config/ConfigReader.h" #include "util/String.h" +#include "util/geo/Geo.h" #include "util/log/Log.h" using pfaedle::config::ConfigReader; @@ -19,7 +20,7 @@ using std::string; using std::exception; using std::vector; -static const char* YEAR = __DATE__ + 7; +static const char* YEAR = &__DATE__[7]; static const char* COPY = "University of Freiburg - Chair of Algorithms and Data Structures"; static const char* AUTHORS = "Patrick Brosi "; @@ -28,7 +29,7 @@ static const char* AUTHORS = "Patrick Brosi "; void ConfigReader::help(const char* bin) { std::cout << std::setfill(' ') << std::left << "pfaedle GTFS map matcher " << VERSION_FULL << "\n(built " << __DATE__ << " " << __TIME__ - << " with geometry precision <" << PFAEDLE_PRECISION_STR << ">)\n\n" + << " with geometry precision <" << PFDL_PREC_STR << ">)\n\n" << "(C) " << YEAR << " " << COPY << "\n" << "Authors: " << AUTHORS << "\n\n" << "Usage: " << bin @@ -43,6 +44,8 @@ void ConfigReader::help(const char* bin) { << "drop shapes already present in the feed and\n" << std::setw(35) << " " << " recalculate them\n" + << std::setw(35) << " --write-colors" + << "write matched route line colors, where missing\n" << "\nInput:\n" << std::setw(35) << " -c [ --config ] arg" << "pfaedle config file\n" @@ -83,26 +86,6 @@ void ConfigReader::help(const char* bin) { << "write routing graph as GeoJSON to\n" << std::setw(35) << " " << " /graph.json\n" - << std::setw(35) << " --write-cgraph" - << "if -T is set, write combination graph as\n" - << std::setw(35) << " " - << " GeoJSON to " - "/combgraph.json\n" - << std::setw(35) << " --method arg (=global)" - << "matching method to use, either 'global'\n" - << std::setw(35) << " " - << " (based on HMM), 'greedy' or " - "'greedy2'\n" - << std::setw(35) << " --eval" - << "evaluate existing shapes against matched\n" - << std::setw(35) << " " - << " shapes and print results\n" - << std::setw(35) << " --eval-path arg (=.)" - << "path for eval file output\n" - << std::setw(35) << " --eval-df-bins arg (= )" - << "bins to use for d_f histogram, comma sep.\n" - << std::setw(35) << " " - << " (e.g. 10,20,30,40)\n" << "\nMisc:\n" << std::setw(35) << " -T [ --trip-id ] arg" << "Do routing only for trip , write result \n" @@ -111,11 +94,19 @@ void ConfigReader::help(const char* bin) { << std::setw(35) << " --overpass" << "Output overpass query for matching OSM data\n" << std::setw(35) << " --grid-size arg (=2000)" - << "Grid cell size\n" - << std::setw(35) << " --use-route-cache" - << "(experimental) cache intermediate routing\n" - << std::setw(35) << " " - << " results\n"; + << "Approx. grid cell size in meters\n" + << std::setw(35) << " --no-fast-hops" + << "Disable fast hops technique\n" + << std::setw(35) << " --no-a-star" + << "Disable A* heuristic \n" + << std::setw(35) << " --no-trie" + << "Disable trip tries \n" + << std::setw(35) << " --no-hop-cache" + << "Disable hop cache \n" + << std::setw(35) << " --stats" + << "write stats to stats.json\n" + << std::setw(35) << " -P" + << "additional parameter string (in cfg file format)\n"; } // _____________________________________________________________________________ @@ -134,46 +125,37 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { {"osm-out", required_argument, 0, 'X'}, {"trip-id", required_argument, 0, 'T'}, {"write-graph", no_argument, 0, 1}, - {"write-cgraph", no_argument, 0, 2}, {"write-trgraph", no_argument, 0, 4}, - {"method", required_argument, 0, 5}, - {"eval", no_argument, 0, 3}, - {"eval-path", required_argument, 0, 6}, - {"eval-df-bins", required_argument, 0, 7}, {"dbg-path", required_argument, 0, 'd'}, {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"inplace", no_argument, 0, 9}, - {"use-route-cache", no_argument, 0, 8}, + {"no-fast-hops", no_argument, 0, 10}, + {"no-a-star", no_argument, 0, 11}, + {"no-trie", no_argument, 0, 12}, + {"write-colors", no_argument, 0, 13}, + {"stats", no_argument, 0, 14}, + {"no-hop-cache", no_argument, 0, 15}, {0, 0, 0, 0}}; char c; - while ((c = getopt_long(argc, argv, ":o:hvi:c:x:Dm:g:X:T:d:p", ops, 0)) != + while ((c = getopt_long(argc, argv, ":o:hvi:c:x:Dm:g:X:T:d:pP:", ops, 0)) != -1) { switch (c) { case 1: cfg->writeGraph = true; break; - case 2: - cfg->writeCombGraph = true; - break; - case 3: - cfg->evaluate = true; - break; case 4: cfg->buildTransitGraph = true; break; - case 5: - cfg->solveMethod = optarg; + case 10: + cfg->noFastHops = true; break; - case 6: - cfg->evalPath = optarg; + case 11: + cfg->noAStar = true; break; - case 7: - cfg->evalDfBins = optarg; - break; - case 8: - cfg->useCaching = true; + case 12: + cfg->noTrie = true; break; case 'o': cfg->outputPath = optarg; @@ -194,7 +176,7 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { motStr = optarg; break; case 'g': - cfg->gridSize = atof(optarg); + cfg->gridSize = atof(optarg) / util::geo::M_PER_DEG; break; case 'X': cfg->writeOsm = optarg; @@ -202,6 +184,9 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { case 'T': cfg->shapeTripId = optarg; break; + case 'P': + cfg->motCfgParam += std::string("\n") + optarg; + break; case 'd': cfg->dbgOutputPath = optarg; break; @@ -211,10 +196,19 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) { case 9: cfg->inPlace = true; break; + case 13: + cfg->writeColors = true; + break; + case 14: + cfg->writeStats = true; + break; + case 15: + cfg->noHopCache = true; + break; case 'v': std::cout << "pfaedle " << VERSION_FULL << " (built " << __DATE__ << " " << __TIME__ << " with geometry precision <" - << PFAEDLE_PRECISION_STR << ">)\n" + << PFDL_PREC_STR << ">)\n" << "(C) " << YEAR << " " << COPY << "\n" << "Authors: " << AUTHORS << "\nGNU General Public " "License v3.0\n"; diff --git a/src/pfaedle/config/MotConfig.h b/src/pfaedle/config/MotConfig.h index 3b7ab48..f99f4eb 100644 --- a/src/pfaedle/config/MotConfig.h +++ b/src/pfaedle/config/MotConfig.h @@ -17,20 +17,11 @@ struct MotConfig { router::MOTs mots; osm::OsmReadOpts osmBuildOpts; router::RoutingOpts routingOpts; - std::map unproced; + std::string transWeight; }; inline bool operator==(const MotConfig& a, const MotConfig& b) { - bool unprocedEq = a.unproced.size() == b.unproced.size(); - for (const auto& kv : a.unproced) { - if (!b.unproced.count(kv.first) || - b.unproced.find(kv.first)->second != kv.second) { - unprocedEq = false; - break; - } - } - return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts && - unprocedEq; + return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts; } } // namespace config diff --git a/src/pfaedle/config/MotConfigReader.cpp b/src/pfaedle/config/MotConfigReader.cpp index 96fce0e..bf1b0cd 100644 --- a/src/pfaedle/config/MotConfigReader.cpp +++ b/src/pfaedle/config/MotConfigReader.cpp @@ -2,28 +2,33 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi +#include #include #include #include "pfaedle/config/MotConfigReader.h" +#include "pfaedle/osm/OsmReadOpts.h" #include "util/Misc.h" #include "util/String.h" #include "util/log/Log.h" -using pfaedle::config::MotConfigReader; -using pfaedle::config::MotConfig; -using pfaedle::osm::FilterRule; -using pfaedle::osm::KeyVal; +using ad::cppgtfs::gtfs::Route; using configparser::ConfigFileParser; using configparser::ParseExc; +using pfaedle::config::MotConfig; +using pfaedle::config::MotConfigReader; using pfaedle::osm::DeepAttrRule; +using pfaedle::osm::FilterRule; +using pfaedle::osm::KeyVal; using pfaedle::trgraph::ReplRules; -using ad::cppgtfs::gtfs::Route; + +double DEF_TRANS_PEN = 0.0083; // _____________________________________________________________________________ MotConfigReader::MotConfigReader() {} // _____________________________________________________________________________ -void MotConfigReader::parse(const std::vector& paths) { +void MotConfigReader::parse(const std::vector& paths, + const std::string& literal) { ConfigFileParser p; // parse explicitely given paths @@ -32,17 +37,33 @@ void MotConfigReader::parse(const std::vector& paths) { p.parse(s); } + if (literal.size()) p.parseStr(literal); + for (const auto& sec : p.getSecs()) { - MotConfig curCfg; + MotConfig cfg; + + cfg.transWeight = "expo"; + std::string secStr = sec.first; if (secStr.empty()) continue; - std::set procedKeys; + + if (p.hasKey(secStr, "routing_transition_method")) { + cfg.routingOpts.transPenMethod = + p.getStr(secStr, "routing_transition_method"); + } else { + cfg.routingOpts.transPenMethod = "exp"; + } + + if (p.hasKey(secStr, "routing_use_stations")) { + cfg.routingOpts.useStations = p.getBool(secStr, "routing_use_stations"); + } else { + cfg.routingOpts.useStations = true; + } if (p.hasKey(secStr, "osm_filter_keep")) { - procedKeys.insert("osm_filter_keep"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_keep", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.keepFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } @@ -50,321 +71,471 @@ void MotConfigReader::parse(const std::vector& paths) { for (uint8_t i = 0; i < 8; i++) { std::string name = std::string("osm_filter_lvl") + std::to_string(i); if (p.hasKey(secStr, name)) { - procedKeys.insert(name); for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert( + cfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } } if (p.hasKey(secStr, "osm_filter_drop")) { - procedKeys.insert("osm_filter_drop"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.dropFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.dropFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_max_snap_level")) { - procedKeys.insert("osm_max_snap_level"); - curCfg.osmBuildOpts.maxSnapLevel = - p.getInt(sec.first, "osm_max_snap_level"); + cfg.osmBuildOpts.maxSnapLevel = p.getInt(sec.first, "osm_max_snap_level"); } else { - curCfg.osmBuildOpts.maxSnapLevel = 7; + cfg.osmBuildOpts.maxSnapLevel = 7; } if (p.hasKey(secStr, "osm_filter_nohup")) { - procedKeys.insert("osm_filter_nohup"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_nohup", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_oneway")) { - procedKeys.insert("osm_filter_oneway"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_oneway", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_oneway_reverse")) { - procedKeys.insert("osm_filter_oneway_reverse"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert( + cfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_undirected")) { - procedKeys.insert("osm_filter_undirected"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_undirected", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_station")) { - procedKeys.insert("osm_filter_station"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_station", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.stationFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.stationFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_station_blocker")) { - procedKeys.insert("osm_filter_station_blocker"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert( + osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); + } + } + + if (p.hasKey(secStr, "osm_filter_turning_cycle")) { + for (const auto& kvs : + p.getStrArr(sec.first, "osm_filter_turning_cycle", ' ')) { + auto fRule = getFRule(kvs); + cfg.osmBuildOpts.turnCycleFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_node_positive_restriction")) { - procedKeys.insert("osm_node_positive_restriction"); for (const auto& kvs : p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert( + cfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_node_negative_restriction")) { - procedKeys.insert("osm_node_negative_restriction"); for (const auto& kvs : p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert( + cfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_filter_no_restriction")) { - procedKeys.insert("osm_filter_no_restriction"); for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) { auto fRule = getFRule(kvs); - curCfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert( + cfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert( osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); } } if (p.hasKey(secStr, "osm_station_name_attrs")) { - procedKeys.insert("osm_station_name_attrs"); for (const std::string& r : p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) { - curCfg.osmBuildOpts.statAttrRules.nameRule.push_back( - getDeepAttrRule(r)); + cfg.osmBuildOpts.statAttrRules.nameRule.push_back(getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_track_number_tags")) { - procedKeys.insert("osm_track_number_tags"); for (const std::string& r : p.getStrArr(sec.first, "osm_track_number_tags", ' ')) { - curCfg.osmBuildOpts.statAttrRules.platformRule.push_back( + cfg.osmBuildOpts.statAttrRules.platformRule.push_back( getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_station_id_attrs")) { - procedKeys.insert("osm_station_id_attrs"); for (const std::string& r : p.getStrArr(sec.first, "osm_station_id_attrs", ' ')) { - curCfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r)); + cfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_edge_track_number_tags")) { - procedKeys.insert("osm_edge_track_number_tags"); for (const std::string& r : p.getStrArr(sec.first, "osm_edge_track_number_tags", ' ')) { - curCfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r)); + cfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r)); } } if (p.hasKey(secStr, "osm_station_group_attrs")) { - procedKeys.insert("osm_station_group_attrs"); - auto arr = p.getStrArr(secStr, "osm_station_group_attrs", ' '); - - for (const auto& ruleStr : arr) { - auto deep = getDeepAttrRule(ruleStr); - // TODO(patrick): getKv is misused here as a a=b parser - auto attrD = getKv(deep.attr); - deep.attr = attrD.first; - double dist = atof(attrD.second.c_str()); - curCfg.osmBuildOpts.statGroupNAttrRules.push_back({deep, dist}); - } + LOG(WARN) << "Option osm_station_group_attrs has been removed."; } + // default value, to enable color writing on old configs + cfg.osmBuildOpts.relLinerules.colorRule = {"colour", "color"}; + if (p.hasKey(secStr, "osm_line_relation_tags")) { - procedKeys.insert("osm_line_relation_tags"); auto arr = p.getStrArr(secStr, "osm_line_relation_tags", ' '); for (const auto& ruleStr : arr) { auto rule = getKv(ruleStr); auto tags = util::split(rule.second, ','); if (rule.first == "from_name") - curCfg.osmBuildOpts.relLinerules.fromNameRule = tags; + cfg.osmBuildOpts.relLinerules.fromNameRule = tags; else if (rule.first == "to_name") - curCfg.osmBuildOpts.relLinerules.toNameRule = tags; + cfg.osmBuildOpts.relLinerules.toNameRule = tags; else if (rule.first == "line_name") - curCfg.osmBuildOpts.relLinerules.sNameRule = tags; + cfg.osmBuildOpts.relLinerules.sNameRule = tags; + else if (rule.first == "line_color") + cfg.osmBuildOpts.relLinerules.colorRule = tags; } } + cfg.osmBuildOpts.maxSnapDistance = 50; if (p.hasKey(secStr, "osm_max_snap_distance")) { - procedKeys.insert("osm_max_snap_distance"); - curCfg.osmBuildOpts.maxSnapDistances = - p.getDoubleArr(secStr, "osm_max_snap_distance", ','); - } else { - curCfg.osmBuildOpts.maxSnapDistances.push_back(50); + auto v = p.getDoubleArr(secStr, "osm_max_snap_distance", ','); + if (v.size()) cfg.osmBuildOpts.maxSnapDistance = v.back(); + } + + cfg.osmBuildOpts.maxStationCandDistance = + cfg.osmBuildOpts.maxSnapDistance * 2; + if (p.hasKey(secStr, "osm_max_station_cand_distance")) { + auto v = p.getDouble(secStr, "osm_max_station_cand_distance"); + cfg.osmBuildOpts.maxStationCandDistance = v; } if (p.hasKey(secStr, "osm_max_snap_fallback_distance")) { - procedKeys.insert("osm_max_snap_fallback_distance"); - curCfg.osmBuildOpts.maxSnapFallbackHeurDistance = - p.getDouble(secStr, "osm_max_snap_fallback_distance"); - } else { - curCfg.osmBuildOpts.maxSnapFallbackHeurDistance = - *std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(), - curCfg.osmBuildOpts.maxSnapDistances.end()) * - 2; + LOG(WARN) << "Option osm_max_snap_fallback_distance has been removed."; } if (p.hasKey(secStr, "osm_max_osm_station_distance")) { - procedKeys.insert("osm_max_osm_station_distance"); - curCfg.osmBuildOpts.maxOsmStationDistance = - p.getDouble(secStr, "osm_max_osm_station_distance"); + double ref = p.getDouble(secStr, "osm_max_osm_station_distance"); + cfg.osmBuildOpts.maxOsmStationDistances.push_back(fmin(5, ref)); + for (double i = 10; i < ref + 1; i += 10) { + cfg.osmBuildOpts.maxOsmStationDistances.push_back(i); + } } else { - curCfg.osmBuildOpts.maxOsmStationDistance = 5; + cfg.osmBuildOpts.maxOsmStationDistances.push_back(5); } if (p.hasKey(secStr, "osm_max_node_block_distance")) { - procedKeys.insert("osm_max_node_block_distance"); - curCfg.osmBuildOpts.maxBlockDistance = + cfg.osmBuildOpts.maxBlockDistance = p.getDouble(secStr, "osm_max_node_block_distance"); } else { - curCfg.osmBuildOpts.maxBlockDistance = - *std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(), - curCfg.osmBuildOpts.maxSnapDistances.end()) / + cfg.osmBuildOpts.maxBlockDistance = + *std::max_element(cfg.osmBuildOpts.maxOsmStationDistances.begin(), + cfg.osmBuildOpts.maxOsmStationDistances.end()) / 8; } + double DEF_SPEED = 85; for (uint8_t i = 0; i < 8; i++) { std::string name = std::string("routing_lvl") + std::to_string(i) + "_fac"; if (p.hasKey(secStr, name)) { - procedKeys.insert(name); - double v = p.getDouble(sec.first, name); - curCfg.routingOpts.levelPunish[i] = v; - } else { - curCfg.routingOpts.levelPunish[i] = 1; + double f = p.getPosDouble(sec.first, name); + LOG(WARN) << "Option " << name << " is deprecated, use osm_lvl" + << std::to_string(i) << "_avg_speed instead."; + double v = DEF_SPEED / f; + LOG(DEBUG) << " (using osm_lvl" << std::to_string(i) << "_avg_speed of " + << v << " instead)"; + cfg.osmBuildOpts.levelDefSpeed[i] = v * 0.2777; // store in m/s } } - if (p.hasKey(secStr, "routing_full_turn_punish")) { - procedKeys.insert("routing_full_turn_punish"); - curCfg.routingOpts.fullTurnPunishFac = - p.getDouble(secStr, "routing_full_turn_punish"); - } - - if (p.hasKey(secStr, "routing_no_self_hops")) { - procedKeys.insert("routing_no_self_hops"); - curCfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops"); - } - - if (p.hasKey(secStr, "routing_full_turn_angle")) { - procedKeys.insert("routing_full_turn_angle"); - double ang = p.getDouble(secStr, "routing_full_turn_angle"); - curCfg.routingOpts.fullTurnAngle = ang; - curCfg.osmBuildOpts.fullTurnAngle = ang; - } else { - curCfg.routingOpts.fullTurnAngle = 5; - curCfg.osmBuildOpts.fullTurnAngle = 5; - } - - if (p.hasKey(secStr, "routing_snap_full_turn_angle")) { - procedKeys.insert("routing_snap_full_turn_angle"); - double ang = p.getDouble(secStr, "routing_snap_full_turn_angle"); - curCfg.osmBuildOpts.maxAngleSnapReach = ang; - } else { - curCfg.osmBuildOpts.maxAngleSnapReach = curCfg.routingOpts.fullTurnAngle; - } - - if (p.hasKey(secStr, "routing_pass_thru_station_punish")) { - procedKeys.insert("routing_pass_thru_station_punish"); - curCfg.routingOpts.passThruStationsPunish = - p.getDouble(secStr, "routing_pass_thru_station_punish"); + for (uint8_t i = 0; i < 8; i++) { + std::string name = + std::string("osm_lvl") + std::to_string(i) + "_avg_speed"; + if (p.hasKey(secStr, name)) { + double v = p.getPosDouble(sec.first, name); + cfg.osmBuildOpts.levelDefSpeed[i] = v * 0.2777; // store in m/s + } } if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) { - procedKeys.insert("routing_one_way_meter_punish_fac"); - curCfg.routingOpts.oneWayPunishFac = - p.getDouble(secStr, "routing_one_way_meter_punish_fac"); + LOG(WARN) << "Option routing_one_way_meter_punish_fac is deprecated, use " + "osm_one_way_speed_penalty_fac instead."; + cfg.osmBuildOpts.oneWaySpeedPen = + 1 + p.getPosDouble(secStr, "routing_one_way_meter_punish_fac"); + LOG(DEBUG) << " (using osm_one_way_speed_penalty_fac of " + << cfg.osmBuildOpts.oneWaySpeedPen << " instead)"; + } else { + cfg.osmBuildOpts.oneWaySpeedPen = 1; } - if (p.hasKey(secStr, "routing_one_way_edge_punish")) { - procedKeys.insert("routing_one_way_edge_punish"); - curCfg.routingOpts.oneWayEdgePunish = - p.getDouble(secStr, "routing_one_way_edge_punish"); + if (p.hasKey(secStr, "osm_one_way_speed_penalty_fac")) { + cfg.osmBuildOpts.oneWaySpeedPen = + p.getPosDouble(secStr, "osm_one_way_speed_penalty_fac"); + } else { + // def already set above } - if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) { - procedKeys.insert("routing_line_unmatched_punish_fac"); - curCfg.routingOpts.lineUnmatchedPunishFact = - p.getDouble(secStr, "routing_line_unmatched_punish_fac"); + if (p.hasKey(secStr, "osm_one_way_entry_cost")) { + cfg.osmBuildOpts.oneWayEntryCost = + p.getPosDouble(secStr, "osm_one_way_entry_cost"); + + } else { + cfg.osmBuildOpts.oneWayEntryCost = 0; } + // take the same cost for taking restricted turns to keep + // configuration simple + double val = cfg.osmBuildOpts.oneWayEntryCost * 10.0; + if (val > std::numeric_limits::max()) { + val = std::numeric_limits::max(); + } + + cfg.routingOpts.turnRestrCost = val; + + if (p.hasKey(secStr, "routing_full_turn_punish")) { + double val = p.getPosDouble(secStr, "routing_full_turn_punish"); + + LOG(WARN) << "Option routing_full_turn_punish is deprecated, use " + "routing_full_turn_penalty instead."; + + val /= cfg.osmBuildOpts.levelDefSpeed[0]; + + LOG(DEBUG) << " (using routing_full_turn_penalty of " << val + << " instead)"; + + val *= 10.0; + + if (val > std::numeric_limits::max()) { + val = std::numeric_limits::max(); + } + + cfg.routingOpts.fullTurnPunishFac = val; + } + + if (p.hasKey(secStr, "routing_full_turn_penalty")) { + double val = p.getPosDouble(secStr, "routing_full_turn_penalty") * 10.0; + + if (val > std::numeric_limits::max()) { + val = std::numeric_limits::max(); + } + + cfg.routingOpts.fullTurnPunishFac = val; + } + + if (p.hasKey(secStr, "routing_no_self_hops")) { + cfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops"); + } + + if (p.hasKey(secStr, "routing_full_turn_angle")) { + double ang = p.getPosDouble(secStr, "routing_full_turn_angle"); + cfg.routingOpts.fullTurnAngle = ang; + cfg.osmBuildOpts.fullTurnAngle = ang; + } else { + cfg.routingOpts.fullTurnAngle = 5; + cfg.osmBuildOpts.fullTurnAngle = 5; + } + + if (p.hasKey(secStr, "routing_snap_full_turn_angle")) { + double ang = p.getPosDouble(secStr, "routing_snap_full_turn_angle"); + cfg.osmBuildOpts.maxAngleSnapReach = ang; + } else { + cfg.osmBuildOpts.maxAngleSnapReach = cfg.routingOpts.fullTurnAngle; + } + + if (p.hasKey(secStr, "routing_pass_thru_station_punish")) { + LOG(WARN) << "Option routing_pass_thru_station_punish has been removed."; + } + + cfg.routingOpts.turnRestrCost *= 10.0; + if (p.hasKey(secStr, "routing_no_lines_punish_fac")) { - procedKeys.insert("routing_no_lines_punish_fac"); - curCfg.routingOpts.noLinesPunishFact = - p.getDouble(secStr, "routing_no_lines_punish_fac"); + LOG(WARN) << "Option routing_no_lines_punish_fac is deprecated, use " + "routing_no_lines_penalty_fac instead."; + + cfg.routingOpts.noLinesPunishFact = + 1 + p.getPosDouble(secStr, "routing_no_lines_punish_fac"); + + LOG(DEBUG) << " (using routing_no_lines_penalty_fac of " + << cfg.routingOpts.noLinesPunishFact << " instead)"; + } else { + cfg.routingOpts.noLinesPunishFact = 1; + } + + if (p.hasKey(secStr, "routing_no_lines_penalty_fac")) { + cfg.routingOpts.noLinesPunishFact = + p.getPosDouble(secStr, "routing_no_lines_penalty_fac"); + } else { + // default already set above + } + + // store this at two places, as we are writing the punishment into the graph + cfg.osmBuildOpts.noLinesPunishFact = cfg.routingOpts.noLinesPunishFact; + + if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) { + LOG(WARN) + << "Option routing_line_unmatched_punish_fac is deprecated, use " + "routing_line_unmatched_time_penalty_fac, " + "routing_line_station_from_unmatched_time_penalty, and " + "routing_line_station_to_unmatched_time_penalty instead."; + + cfg.routingOpts.lineUnmatchedPunishFact = + 1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3; + + cfg.routingOpts.lineNameFromUnmatchedPunishFact = + 1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3; + + cfg.routingOpts.lineNameToUnmatchedPunishFact = + 1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3; + + LOG(DEBUG) << " (using routing_line_unmatched_punish_fac of " + << cfg.routingOpts.lineUnmatchedPunishFact << " instead)"; + LOG(DEBUG) + << " (using routing_line_station_from_unmatched_time_penalty of " + << cfg.routingOpts.lineNameFromUnmatchedPunishFact << " instead)"; + LOG(DEBUG) << " (using routing_line_station_to_unmatched_time_penalty of " + << cfg.routingOpts.lineNameToUnmatchedPunishFact + << " instead)"; + } + + if (p.hasKey(secStr, "routing_line_unmatched_time_penalty_fac")) { + cfg.routingOpts.lineUnmatchedPunishFact = + p.getPosDouble(secStr, "routing_line_unmatched_time_penalty_fac"); + } + + if (p.hasKey(secStr, "routing_line_station_from_unmatched_time_penalty")) { + cfg.routingOpts.lineNameFromUnmatchedPunishFact = p.getPosDouble( + secStr, "routing_line_station_from_unmatched_time_penalty"); + } + + if (p.hasKey(secStr, "routing_line_station_to_unmatched_time_penalty")) { + cfg.routingOpts.lineNameToUnmatchedPunishFact = p.getPosDouble( + secStr, "routing_line_station_to_unmatched_time_penalty"); } if (p.hasKey(secStr, "routing_platform_unmatched_punish")) { - procedKeys.insert("routing_platform_unmatched_punish"); - curCfg.routingOpts.platformUnmatchedPen = - p.getDouble(secStr, "routing_platform_unmatched_punish"); + LOG(WARN) + << "Option routing_platform_unmatched_punish is deprecated, use " + "routing_platform_unmatched_penalty instead."; + cfg.routingOpts.platformUnmatchedPen = + p.getPosDouble(secStr, "routing_platform_unmatched_punish"); + + cfg.routingOpts.platformUnmatchedPen = + cfg.routingOpts.platformUnmatchedPen * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + + LOG(DEBUG) << " (using routing_platform_unmatched_penalty of " + << cfg.routingOpts.platformUnmatchedPen << " instead)"; + } else { + cfg.routingOpts.platformUnmatchedPen = 0; } - if (p.hasKey(secStr, "routing_non_osm_station_punish")) { - procedKeys.insert("routing_non_osm_station_punish"); - curCfg.routingOpts.nonOsmPen = - p.getDouble(secStr, "routing_non_osm_station_punish"); + if (p.hasKey(secStr, "routing_platform_unmatched_penalty")) { + cfg.routingOpts.platformUnmatchedPen = + p.getPosDouble(secStr, "routing_platform_unmatched_penalty"); } else { - curCfg.routingOpts.nonOsmPen = 0; + // default already set above + } + + if (p.hasKey(secStr, "routing_transition_penalty_fac")) { + cfg.routingOpts.transitionPen = + p.getPosDouble(secStr, "routing_transition_penalty_fac"); + } else { + cfg.routingOpts.transitionPen = DEF_TRANS_PEN; } if (p.hasKey(secStr, "routing_station_distance_punish_fac")) { - procedKeys.insert("routing_station_distance_punish_fac"); - curCfg.routingOpts.stationDistPenFactor = - p.getDouble(secStr, "routing_station_distance_punish_fac"); + cfg.routingOpts.stationDistPenFactor = + p.getPosDouble(secStr, "routing_station_distance_punish_fac"); + LOG(WARN) << "Option routing_station_distance_punish_fac is deprecated, " + "use routing_station_move_penalty_fac instead."; + cfg.routingOpts.stationDistPenFactor = + cfg.routingOpts.stationDistPenFactor * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + LOG(DEBUG) << " (using routing_station_move_penalty_fac of " + << cfg.routingOpts.stationDistPenFactor << " instead)"; } else { - curCfg.routingOpts.stationDistPenFactor = 1; + cfg.routingOpts.stationDistPenFactor = + cfg.routingOpts.stationDistPenFactor * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + } + + if (p.hasKey(secStr, "routing_station_move_penalty_fac")) { + cfg.routingOpts.stationDistPenFactor = + p.getPosDouble(secStr, "routing_station_move_penalty_fac"); + } else { + // the default value was already set above + } + + if (p.hasKey(secStr, "routing_non_osm_station_punish")) { + cfg.routingOpts.nonStationPen = + p.getPosDouble(secStr, "routing_non_osm_station_punish"); + LOG(WARN) << "Option routing_non_osm_station_punish is deprecated, use " + "routing_non_station_penalty instead."; + cfg.routingOpts.nonStationPen = + cfg.routingOpts.nonStationPen * + (DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]); + LOG(DEBUG) << " (using routing_non_station_penalty of " + << cfg.routingOpts.nonStationPen << " instead)"; + } else { + cfg.routingOpts.nonStationPen = 0; + } + + if (p.hasKey(secStr, "routing_non_station_penalty")) { + cfg.routingOpts.nonStationPen = + p.getPosDouble(secStr, "routing_non_station_penalty"); + } else { + // default was already set above + } + + if (p.hasKey(secStr, "routing_station_unmatched_penalty")) { + cfg.routingOpts.stationUnmatchedPen = + p.getPosDouble(secStr, "routing_station_unmatched_penalty"); + } else { + cfg.routingOpts.stationUnmatchedPen = cfg.routingOpts.nonStationPen / 2; } if (p.hasKey(secStr, "station_normalize_chain")) { - procedKeys.insert("station_normalize_chain"); try { auto arr = p.getStrArr(secStr, "station_normalize_chain", ';'); - curCfg.osmBuildOpts.statNormzer = - trgraph::Normalizer(getNormRules(arr)); + cfg.osmBuildOpts.statNormzer = trgraph::Normalizer(getNormRules(arr)); } catch (const std::exception& e) { throw ParseExc(p.getVal(secStr, "station_normalize_chain").line, p.getVal(secStr, "station_normalize_chain").pos, @@ -375,11 +546,9 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "track_normalize_chain")) { - procedKeys.insert("track_normalize_chain"); try { auto arr = p.getStrArr(secStr, "track_normalize_chain", ';'); - curCfg.osmBuildOpts.trackNormzer = - trgraph::Normalizer(getNormRules(arr)); + cfg.osmBuildOpts.trackNormzer = trgraph::Normalizer(getNormRules(arr)); } catch (const std::exception& e) { throw ParseExc(p.getVal(secStr, "track_normalize_chain").line, p.getVal(secStr, "track_normalize_chain").pos, @@ -390,11 +559,9 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "line_normalize_chain")) { - procedKeys.insert("line_normalize_chain"); try { auto arr = p.getStrArr(secStr, "line_normalize_chain", ';'); - curCfg.osmBuildOpts.lineNormzer = - trgraph::Normalizer(getNormRules(arr)); + cfg.osmBuildOpts.lineNormzer = trgraph::Normalizer(getNormRules(arr)); } catch (const std::exception& e) { throw ParseExc(p.getVal(secStr, "line_normalize_chain").line, p.getVal(secStr, "line_normalize_chain").pos, @@ -405,10 +572,9 @@ void MotConfigReader::parse(const std::vector& paths) { } if (p.hasKey(secStr, "station_id_normalize_chain")) { - procedKeys.insert("station_id_normalize_chain"); try { auto arr = p.getStrArr(secStr, "station_id_normalize_chain", ';'); - curCfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr)); + cfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr)); } catch (const std::exception& e) { throw ParseExc(p.getVal(secStr, "station_id_normalize_chain").line, p.getVal(secStr, "station_id_normalize_chain").pos, @@ -418,18 +584,41 @@ void MotConfigReader::parse(const std::vector& paths) { } } - for (const auto& kv : p.getKeyVals(secStr)) { - if (!procedKeys.count(kv.first)) - curCfg.unproced[kv.first] = kv.second.val; + // determine the maximum possible speed for this config, this is later + // used to filter out station which are so far out of reach we don't + // have to consider them for the bounding box calculation + cfg.osmBuildOpts.maxSpeed = 0; + cfg.osmBuildOpts.maxSpeedCorFac = 1; + for (size_t i = 0; i < 8; i++) { + if (cfg.osmBuildOpts.levelDefSpeed[i] > cfg.osmBuildOpts.maxSpeed) + cfg.osmBuildOpts.maxSpeed = cfg.osmBuildOpts.levelDefSpeed[i]; } + if (cfg.routingOpts.lineUnmatchedPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= + cfg.routingOpts.lineUnmatchedPunishFact; + if (cfg.routingOpts.lineNameFromUnmatchedPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= + cfg.routingOpts.lineNameFromUnmatchedPunishFact; + if (cfg.routingOpts.lineNameToUnmatchedPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= + cfg.routingOpts.lineNameToUnmatchedPunishFact; + + if (cfg.routingOpts.noLinesPunishFact < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= cfg.routingOpts.noLinesPunishFact; + + if (cfg.osmBuildOpts.oneWaySpeedPen < 1) + cfg.osmBuildOpts.maxSpeedCorFac *= cfg.osmBuildOpts.oneWaySpeedPen; + + cfg.osmBuildOpts.maxSpeed /= cfg.osmBuildOpts.maxSpeedCorFac; + bool found = false; - for (auto& cfg : _cfgs) { - if (cfg == curCfg) { + for (auto& exCfg : _cfgs) { + if (cfg == exCfg) { for (auto mot : ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr)) { - cfg.mots.insert(mot); + exCfg.mots.insert(mot); } found = true; break; @@ -437,8 +626,8 @@ void MotConfigReader::parse(const std::vector& paths) { } if (!found) { - curCfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr); - _cfgs.push_back(curCfg); + cfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr); + _cfgs.push_back(cfg); } } } diff --git a/src/pfaedle/config/MotConfigReader.h b/src/pfaedle/config/MotConfigReader.h index 51716ff..86e3ffb 100644 --- a/src/pfaedle/config/MotConfigReader.h +++ b/src/pfaedle/config/MotConfigReader.h @@ -23,7 +23,7 @@ using ad::cppgtfs::gtfs::Route; class MotConfigReader { public: MotConfigReader(); - void parse(const std::vector& paths); + void parse(const std::vector& paths, const std::string& literal); const std::vector& getConfigs() const; diff --git a/src/pfaedle/config/PfaedleConfig.h b/src/pfaedle/config/PfaedleConfig.h index e5bc41a..feaa645 100644 --- a/src/pfaedle/config/PfaedleConfig.h +++ b/src/pfaedle/config/PfaedleConfig.h @@ -5,10 +5,11 @@ #ifndef PFAEDLE_CONFIG_PFAEDLECONFIG_H_ #define PFAEDLE_CONFIG_PFAEDLECONFIG_H_ +#include #include #include #include -#include +#include "util/geo/Geo.h" #include "ad/cppgtfs/gtfs/Route.h" namespace pfaedle { @@ -20,38 +21,44 @@ struct Config { Config() : dbgOutputPath("."), solveMethod("global"), - evalPath("."), outputPath("gtfs-out"), dropShapes(false), useHMM(false), writeGraph(false), - writeCombGraph(false), - evaluate(false), buildTransitGraph(false), useCaching(false), writeOverpass(false), inPlace(false), - gridSize(2000) {} + writeColors(false), + noFastHops(false), + noAStar(false), + noTrie(false), + noHopCache(false), + writeStats(false), + gridSize(2000 / util::geo::M_PER_DEG) {} std::string dbgOutputPath; std::string solveMethod; - std::string evalPath; std::string shapeTripId; std::string outputPath; std::string writeOsm; std::string osmPath; - std::string evalDfBins; + std::string motCfgParam; std::vector feedPaths; std::vector configPaths; std::set mots; bool dropShapes; bool useHMM; bool writeGraph; - bool writeCombGraph; - bool evaluate; bool buildTransitGraph; bool useCaching; bool writeOverpass; bool inPlace; + bool writeColors; + bool noFastHops; + bool noAStar; + bool noTrie; + bool noHopCache; + bool writeStats; double gridSize; std::string toString() { @@ -64,10 +71,16 @@ struct Config { << "drop-shapes: " << dropShapes << "\n" << "use-hmm: " << useHMM << "\n" << "write-graph: " << writeGraph << "\n" - << "write-cgraph: " << writeCombGraph << "\n" << "grid-size: " << gridSize << "\n" << "use-cache: " << useCaching << "\n" << "write-overpass: " << writeOverpass << "\n" + << "inplace: " << inPlace << "\n" + << "write-colors: " << writeColors << "\n" + << "no-fast-hops: " << noFastHops << "\n" + << "no-a-star: " << noAStar << "\n" + << "no-trie: " << noTrie << "\n" + << "no-hop-cache: " << noHopCache << "\n" + << "write-stats: " << writeStats << "\n" << "feed-paths: "; for (const auto& p : feedPaths) { diff --git a/src/pfaedle/eval/Collector.cpp b/src/pfaedle/eval/Collector.cpp deleted file mode 100644 index 709830b..0000000 --- a/src/pfaedle/eval/Collector.cpp +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#include -#include -#include -#include -#include -#include "ad/cppgtfs/gtfs/Feed.h" -#include "pfaedle/Def.h" -#include "pfaedle/eval/Collector.h" -#include "pfaedle/eval/Result.h" -#include "util/geo/Geo.h" -#include "util/geo/PolyLine.h" -#include "util/geo/output/GeoJsonOutput.h" -#include "util/log/Log.h" - -using util::geo::PolyLine; - -using pfaedle::gtfs::Trip; -using ad::cppgtfs::gtfs::Shape; -using pfaedle::eval::Collector; -using pfaedle::eval::Result; -using util::geo::output::GeoJsonOutput; - -// _____________________________________________________________________________ -double Collector::add(const Trip* t, const Shape* oldS, const Shape& newS, - const std::vector& newTripDists) { - if (!oldS) { - _noOrigShp++; - return 0; - } - - for (auto st : t->getStopTimes()) { - if (st.getShapeDistanceTravelled() < 0) { - // we cannot safely compare trips without shape dist travelled - // info - _noOrigShp++; - return 0; - } - } - - double fd = 0; - size_t unmatchedSegments; - double unmatchedSegmentsLength; - - std::vector oldDists; - LINE oldL = getWebMercLine( - oldS, t->getStopTimes().begin()->getShapeDistanceTravelled(), - (--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists); - - std::vector newDists; - LINE newL = getWebMercLine(&newS, -1, -1, &newDists); - - std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json"); - GeoJsonOutput gjout(fstr); - - auto oldSegs = segmentize(t, oldL, oldDists, 0); - auto newSegs = segmentize(t, newL, newDists, &newTripDists); - - // cut both result at the beginning and end to clear evaluation from - // loops at the end - POLYLINE oldStart = oldSegs[0]; - POLYLINE newStart = newSegs[0]; - auto oldStartNew = - oldStart.getSegment(oldStart.projectOn(newSegs[0][0]).totalPos, 1); - auto newStartNew = - newStart.getSegment(newStart.projectOn(oldSegs[0][0]).totalPos, 1); - if (fabs(oldStartNew.getLength() - oldStart.getLength()) / - oldStart.getLength() < - 0.5 && - fabs(newStartNew.getLength() - newStart.getLength()) / - newStart.getLength() < - 0.5) { - oldSegs[0] = oldStartNew.getLine(); - newSegs[0] = newStartNew.getLine(); - } - - POLYLINE oldEnd = oldSegs[oldSegs.size() - 1]; - POLYLINE newEnd = newSegs[oldSegs.size() - 1]; - auto oldEndNew = - oldEnd.getSegment(0, oldEnd.projectOn(newSegs.back().back()).totalPos); - auto newEndNew = - newEnd.getSegment(0, newEnd.projectOn(oldSegs.back().back()).totalPos); - if (fabs(oldEndNew.getLength() - oldEnd.getLength()) / oldEnd.getLength() < - 0.5 && - fabs(newEndNew.getLength() - newEnd.getLength()) / newEnd.getLength() < - 0.5) { - oldSegs[oldSegs.size() - 1] = oldEndNew.getLine(); - newSegs[newSegs.size() - 1] = newEndNew.getLine(); - } - - // check for suspicious (most likely erroneous) lines in the - // ground truth data which have a long straight-line segment - - for (auto oldL : oldSegs) { - for (size_t i = 1; i < oldL.size(); i++) { - if (util::geo::webMercMeterDist(oldL[i - 1], oldL[i]) > 500) { - // return 0; - } - } - } - - // new lines build from cleaned-up shapes - LINE oldLCut; - LINE newLCut; - - for (auto oldL : oldSegs) { - gjout.printLatLng(oldL, util::json::Dict{{"ver", "old"}}); - oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end()); - } - - for (auto newL : newSegs) { - gjout.printLatLng(newL, util::json::Dict{{"ver", "new"}}); - newLCut.insert(newLCut.end(), newL.begin(), newL.end()); - } - - gjout.flush(); - fstr.close(); - - double fac = cos(2 * atan(exp((oldSegs.front().front().getY() + - oldSegs.back().back().getY()) / - 6378137.0)) - - 1.5707965); - - if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS.getId())) { - fd = _dCache[oldS][newS.getId()]; - } else { - fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac; - _dCache[oldS][newS.getId()] = fd; - } - - if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS.getId())) { - unmatchedSegments = _dACache[oldS][newS.getId()].first; - unmatchedSegmentsLength = _dACache[oldS][newS.getId()].second; - } else { - auto dA = getDa(oldSegs, newSegs); - _dACache[oldS][newS.getId()] = dA; - unmatchedSegments = dA.first; - unmatchedSegmentsLength = dA.second; - } - - double totL = 0; - for (auto l : oldSegs) totL += util::geo::len(l) * fac; - - // filter out shapes with a lenght of under 5 meters - they are most likely - // artifacts - if (totL < 5) { - _noOrigShp++; - return 0; - } - - _fdSum += fd / totL; - _unmatchedSegSum += unmatchedSegments; - _unmatchedSegLengthSum += unmatchedSegmentsLength; - _results.insert(Result(t, fd / totL)); - _resultsAN.insert(Result(t, static_cast(unmatchedSegments) / - static_cast(oldSegs.size()))); - _resultsAL.insert(Result(t, unmatchedSegmentsLength / totL)); - - LOG(DEBUG) << "This result (" << t->getId() - << "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size() - << " = " - << static_cast(unmatchedSegments) / - static_cast(oldSegs.size()) - << " A_L/L = " << unmatchedSegmentsLength << "/" << totL << " = " - << unmatchedSegmentsLength / totL << " d_f = " << fd; - - return fd; -} - -// _____________________________________________________________________________ -std::vector Collector::segmentize( - const Trip* t, const LINE& shape, const std::vector& dists, - const std::vector* newTripDists) { - std::vector ret; - - if (t->getStopTimes().size() < 2) return ret; - - POLYLINE pl(shape); - std::vector > cuts; - - size_t i = 0; - for (auto st : t->getStopTimes()) { - if (newTripDists) { - cuts.push_back(std::pair( - util::geo::latLngToWebMerc(st.getStop()->getLat(), - st.getStop()->getLng()), - (*newTripDists)[i])); - } else { - cuts.push_back(std::pair( - util::geo::latLngToWebMerc(st.getStop()->getLat(), - st.getStop()->getLng()), - st.getShapeDistanceTravelled())); - } - i++; - } - - // get first half of geometry, and search for start point there! - size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) - - dists.begin(); - if (before + 1 > shape.size()) before = shape.size() - 1; - assert(shape.begin() + before + 1 <= shape.end()); - POLYLINE l(LINE(shape.begin(), shape.begin() + before + 1)); - auto lastLp = l.projectOn(cuts.front().first); - - for (size_t i = 1; i < cuts.size(); i++) { - size_t before = shape.size(); - if (i < cuts.size() - 1 && cuts[i + 1].second > -0.5) { - before = - std::upper_bound(dists.begin(), dists.end(), cuts[i + 1].second) - - dists.begin(); - } - - POLYLINE beforePl(LINE(shape.begin(), shape.begin() + before)); - - auto curLp = beforePl.projectOnAfter(cuts[i].first, lastLp.lastIndex); - - ret.push_back(pl.getSegment(lastLp, curLp).getLine()); - lastLp = curLp; - } - - // std::raise(SIGABRT); - return ret; -} - -// _____________________________________________________________________________ -LINE Collector::getWebMercLine(const Shape* s, double from, double t) { - return getWebMercLine(s, from, t, 0); -} - -// _____________________________________________________________________________ -LINE Collector::getWebMercLine(const Shape* s, double from, double to, - std::vector* dists) { - LINE ret; - - auto i = s->getPoints().begin(); - - for (; i != s->getPoints().end(); i++) { - auto p = *i; - - if ((from < 0 || (p.travelDist - from) > -0.01)) { - if (to >= 0 && (p.travelDist - to) > 0.01) break; - - POINT mercP = util::geo::latLngToWebMerc(p.lat, p.lng); - - ret.push_back(mercP); - if (dists) dists->push_back(p.travelDist); - } - } - - return ret; -} - -// _____________________________________________________________________________ -const std::set& Collector::getResults() const { return _results; } - -// _____________________________________________________________________________ -double Collector::getAvgDist() const { return _fdSum / _results.size(); } - -// _____________________________________________________________________________ -void Collector::printHisto(std::ostream* os, const std::set& result, - const std::vector& bins) const { - size_t W = 60; - - auto it = result.begin(); - std::vector > res; - std::vector examples; - size_t maxC = 0; - - for (size_t i = 0; i < bins.size(); i++) { - size_t c = 0; - const Trip* trip = 0; - - while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) { - if (!trip) trip = it->getTrip(); - c++; - it++; - } - - if (c > maxC) maxC = c; - - examples.push_back(trip); - res.push_back(std::pair(bins[i], c)); - } - - size_t j = 0; - for (auto r : res) { - std::string range = util::toString(r.first); - (*os) << " < " << std::setfill(' ') << std::setw(10) << range << ": "; - size_t i = 0; - - for (; i < W * (static_cast(r.second) / static_cast(maxC)); - i++) { - (*os) << "|"; - } - - if (r.second) - (*os) << " (" << r.second << ", e.g. #" << examples[j]->getId() << ")"; - (*os) << std::endl; - j++; - } -} - -// _____________________________________________________________________________ -std::vector Collector::getBins(double mind, double maxd, size_t steps) { - double bin = (maxd - mind) / steps; - double curE = mind + bin; - - std::vector ret; - while (curE <= maxd) { - ret.push_back(curE); - curE += bin; - } - return ret; -} - -// _____________________________________________________________________________ -void Collector::printCsv(std::ostream* os, const std::set& result, - const std::vector& bins) const { - auto it = result.begin(); - std::vector > res; - - for (size_t i = 0; i < bins.size(); i++) { - size_t c = 0; - const Trip* trip = 0; - - while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) { - if (!trip) trip = it->getTrip(); - c++; - it++; - } - - res.push_back(std::pair(bins[i], c)); - } - - (*os) << "range, count\n"; - for (auto r : res) { - (*os) << r.first << "," << r.second << "\n"; - } -} - -// _____________________________________________________________________________ -void Collector::printStats(std::ostream* os) const { - size_t buckets = 10; - (*os) << "\n ===== Evalution results =====\n\n"; - - (*os) << std::setfill(' ') << std::setw(30) - << " # of trips new shapes were matched for: " << _results.size() - << "\n"; - (*os) << std::setw(30) << " # of trips without input shapes: " << _noOrigShp - << "\n"; - - if (_results.size()) { - (*os) << std::setw(30) << " highest distance to input shapes: " - << (--_results.end())->getDist() << " (on trip #" - << (--_results.end())->getTrip()->getId() << ")\n"; - (*os) << std::setw(30) << " lowest distance to input shapes: " - << (_results.begin())->getDist() << " (on trip #" - << (_results.begin())->getTrip()->getId() << ")\n"; - (*os) << std::setw(30) << " avg total frechet distance: " << getAvgDist() - << "\n"; - - std::vector dfBins = getBins( - (_results.begin())->getDist(), (--_results.end())->getDist(), buckets); - - if (_dfBins.size()) dfBins = _dfBins; - - (*os) << "\n -- Histogram of d_f for this run -- " << std::endl; - printHisto(os, _results, dfBins); - - std::ofstream fstr1(_evalOutPath + "/eval-frechet.csv"); - printCsv(&fstr1, _results, dfBins); - - (*os) << "\n\n\n -- Histogram of A_N/N for this run -- " << std::endl; - printHisto(os, _resultsAN, - getBins((_resultsAN.begin())->getDist(), - (--_resultsAN.end())->getDist(), buckets)); - std::ofstream fstr2(_evalOutPath + "/eval-AN.csv"); - printCsv(&fstr2, _resultsAN, getBins(0, 1, 20)); - - (*os) << "\n\n\n -- Histogram of A_L/L for this run -- " << std::endl; - printHisto(os, _resultsAL, - getBins((_resultsAL.begin())->getDist(), - (--_resultsAL.end())->getDist(), buckets)); - std::ofstream fstr3(_evalOutPath + "/eval-AL.csv"); - printCsv(&fstr3, _resultsAL, getBins(0, 1, 20)); - } - - (*os) << "\n ===== End of evaluation results =====\n"; - (*os) << std::endl; -} - -// _____________________________________________________________________________ -std::pair Collector::getDa(const std::vector& a, - const std::vector& b) { - assert(a.size() == b.size()); - std::pair ret{0, 0}; - - // euclidean distance on web mercator is in meters on equator, - // and proportional to cos(lat) in both y directions - double fac = - cos(2 * atan(exp((a.front().front().getY() + a.back().back().getY()) / - 6378137.0)) - - 1.5707965); - - for (size_t i = 0; i < a.size(); i++) { - double fd = util::geo::frechetDist(a[i], b[i], 3 / fac) * fac; - if (fd >= 20) { - ret.first++; - ret.second += util::geo::len(a[i]) * fac; - } - } - - return ret; -} diff --git a/src/pfaedle/gtfs/Feed.h b/src/pfaedle/gtfs/Feed.h index 8e5cc7b..dd4abdd 100644 --- a/src/pfaedle/gtfs/Feed.h +++ b/src/pfaedle/gtfs/Feed.h @@ -6,7 +6,6 @@ #define PFAEDLE_GTFS_FEED_H_ #include -#include "Route.h" #include "Service.h" #include "ShapeContainer.h" #include "StopTime.h" @@ -21,14 +20,15 @@ namespace pfaedle { namespace gtfs { typedef ad::cppgtfs::gtfs::FeedB< - ad::cppgtfs::gtfs::Agency, Route, ad::cppgtfs::gtfs::Stop, Service, - StopTime, Shape, ad::cppgtfs::gtfs::Fare, ad::cppgtfs::gtfs::Container, - ad::cppgtfs::gtfs::ContContainer, ad::cppgtfs::gtfs::NullContainer, - ad::cppgtfs::gtfs::ContContainer, ad::cppgtfs::gtfs::ContContainer, - ShapeContainer, ad::cppgtfs::gtfs::NullContainer> + ad::cppgtfs::gtfs::Agency, ad::cppgtfs::gtfs::Route, + ad::cppgtfs::gtfs::Stop, Service, StopTime, Shape, ad::cppgtfs::gtfs::Fare, + ad::cppgtfs::gtfs::Container, ad::cppgtfs::gtfs::Container, + ad::cppgtfs::gtfs::NullContainer, ad::cppgtfs::gtfs::ContContainer, + ad::cppgtfs::gtfs::ContContainer, ShapeContainer, + ad::cppgtfs::gtfs::Container> Feed; typedef ad::cppgtfs::gtfs::TripB, Service, - Route, Shape> + ad::cppgtfs::gtfs::Route, Shape> Trip; } // namespace gtfs diff --git a/src/pfaedle/gtfs/Route.h b/src/pfaedle/gtfs/Route.h deleted file mode 100644 index 15ba7f0..0000000 --- a/src/pfaedle/gtfs/Route.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_GTFS_ROUTE_H_ -#define PFAEDLE_GTFS_ROUTE_H_ - -#include -#include -#include -#include -#include -#include -#include "ad/cppgtfs/gtfs/Agency.h" -#include "ad/cppgtfs/gtfs/Route.h" -#include "util/Misc.h" - -using std::exception; -using std::string; - -namespace pfaedle { -namespace gtfs { - -class Route { - public: - typedef Route* Ref; - static std::string getId(Ref r) { return r->getId(); } - - Route() {} - - Route(const string& id, ad::cppgtfs::gtfs::Agency* agency, - const string& short_name, const string& long_name, const string& desc, - ad::cppgtfs::gtfs::flat::Route::TYPE type, const string& url, - uint32_t color, uint32_t text_color) - : _id(id), _short_name(short_name), _long_name(long_name), _type(type) { - UNUSED(agency); - UNUSED(desc); - UNUSED(url); - UNUSED(color); - UNUSED(text_color); - } - - const std::string& getId() const { return _id; } - - const std::string& getShortName() const { return _short_name; } - - const std::string& getLongName() const { return _long_name; } - - ad::cppgtfs::gtfs::flat::Route::TYPE getType() const { return _type; } - - private: - string _id; - string _short_name; - string _long_name; - ad::cppgtfs::gtfs::flat::Route::TYPE _type; -}; - -} // namespace gtfs -} // namespace pfaedle - -#endif // PFAEDLE_GTFS_ROUTE_H_ diff --git a/src/pfaedle/gtfs/ShapeContainer.tpp b/src/pfaedle/gtfs/ShapeContainer.tpp index 60f2b61..9366028 100644 --- a/src/pfaedle/gtfs/ShapeContainer.tpp +++ b/src/pfaedle/gtfs/ShapeContainer.tpp @@ -45,15 +45,15 @@ bool ShapeContainer::remove(const std::string& id) { // ____________________________________________________________________________ template T* ShapeContainer::get(const std::string& id) { - if (!has(id)) return 0; - return reinterpret_cast(1); + UNUSED(id); + return reinterpret_cast(0); } // ____________________________________________________________________________ template const T* ShapeContainer::get(const std::string& id) const { - if (!has(id)) return 0; - return reinterpret_cast(1); + UNUSED(id); + return reinterpret_cast(0); } // ____________________________________________________________________________ diff --git a/src/pfaedle/gtfs/StopTime.h b/src/pfaedle/gtfs/StopTime.h index fe18a52..b8a45b1 100644 --- a/src/pfaedle/gtfs/StopTime.h +++ b/src/pfaedle/gtfs/StopTime.h @@ -27,14 +27,11 @@ class StopTime { ad::cppgtfs::gtfs::flat::StopTime::PU_DO_TYPE put, ad::cppgtfs::gtfs::flat::StopTime::PU_DO_TYPE dot, float distTrav, bool isTp) - : _s(s), _sequence(seq), _dist(distTrav) { - UNUSED(at); - UNUSED(dt); + : _s(s), _sequence(seq), _dist(distTrav), _at(at), _dt(dt), _isTp(isTp) { UNUSED(hs); UNUSED(put); UNUSED(dot); UNUSED(distTrav); - UNUSED(isTp); } const typename StopT::Ref getStop() const { return _s; } @@ -42,20 +39,23 @@ class StopTime { void setShapeDistanceTravelled(double d) { _dist = d; } ad::cppgtfs::gtfs::Time getArrivalTime() const { - return ad::cppgtfs::gtfs::Time(0, 0, 0); + return _at; } ad::cppgtfs::gtfs::Time getDepartureTime() const { - return ad::cppgtfs::gtfs::Time(0, 0, 0); + return _dt; } float getShapeDistanceTravelled() const { return _dist; } uint16_t getSeq() const { return _sequence; } + bool isTp() const { return _isTp; } private: typename StopT::Ref _s; uint32_t _sequence; float _dist; + ad::cppgtfs::gtfs::Time _at, _dt; + bool _isTp; }; template diff --git a/src/pfaedle/gtfs/Writer.cpp b/src/pfaedle/gtfs/Writer.cpp index 7a0c5b3..26dd51d 100644 --- a/src/pfaedle/gtfs/Writer.cpp +++ b/src/pfaedle/gtfs/Writer.cpp @@ -13,13 +13,13 @@ #include "ad/util/CsvWriter.h" #include "pfaedle/gtfs/Writer.h" -using ad::util::CsvWriter; using ad::cppgtfs::Parser; -using pfaedle::gtfs::Writer; +using ad::util::CsvWriter; using pfaedle::getTmpFName; +using pfaedle::gtfs::Writer; // ____________________________________________________________________________ -bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { +void Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { std::ofstream fs; std::ifstream is; std::string gtfsPath(path); @@ -59,8 +59,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeCalendar(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str()); @@ -72,8 +71,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeCalendarDates(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/transfers.txt").c_str()); @@ -85,8 +83,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeTransfers(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str()); @@ -98,8 +95,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFares(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.open((sourceFeed->getPath() + "/fare_rules.txt").c_str()); @@ -111,8 +107,7 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFareRules(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.close(); @@ -142,14 +137,14 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFrequencies(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } is.close(); curFile = getTmpFName(gtfsPath, "stop_times.txt"); curFileTg = gtfsPath + "/stop_times.txt"; fs.open(curFile.c_str()); + if (!fs.good()) cannotWrite(curFile, curFileTg); writeStopTimes(sourceFeed, &fs); fs.close(); @@ -163,15 +158,12 @@ bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const { if (!fs.good()) cannotWrite(curFile, curFileTg); writeFeedInfo(sourceFeed, &fs); fs.close(); - if (std::rename(curFile.c_str(), curFileTg.c_str())) - cannotWrite(curFileTg); + if (std::rename(curFile.c_str(), curFileTg.c_str())) cannotWrite(curFileTg); } - - return true; } // ____________________________________________________________________________ -bool Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const { +void Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const { auto csvw = ad::cppgtfs::Writer::getFeedInfoCsvw(os); csvw.flushLine(); csvw.writeString(f->getPublisherName()); @@ -187,12 +179,10 @@ bool Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const { csvw.skip(); csvw.writeString(f->getVersion()); csvw.flushLine(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/agency.txt").c_str()); @@ -210,12 +200,10 @@ bool Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeAgency(fa, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/stops.txt").c_str()); @@ -233,35 +221,22 @@ bool Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeStop(s, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeRoutes(gtfs::Feed* sourceFeed, std::ostream* os) const { - std::ifstream fs; - fs.open((sourceFeed->getPath() + "/routes.txt").c_str()); - - CsvParser csvp(&fs); - Parser p; +void Writer::writeRoutes(gtfs::Feed* sourceFeed, std::ostream* os) const { ad::cppgtfs::Writer w; CsvWriter csvw = ad::cppgtfs::Writer::getRoutesCsvw(os); csvw.flushLine(); - ad::cppgtfs::gtfs::flat::Route s; - auto flds = Parser::getRouteFlds(&csvp); - - while (p.nextRoute(&csvp, &s, flds)) { - w.writeRoute(s, &csvw); + for (auto r : sourceFeed->getRoutes()) { + w.writeRoute(r.second->getFlat(), &csvw); } - fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/calendar.txt").c_str()); @@ -279,12 +254,10 @@ bool Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeCalendar(c, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeCalendarDates(gtfs::Feed* sourceFeed, +void Writer::writeCalendarDates(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str()); @@ -303,12 +276,10 @@ bool Writer::writeCalendarDates(gtfs::Feed* sourceFeed, w.writeCalendarDate(c, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/frequencies.txt").c_str()); @@ -326,12 +297,10 @@ bool Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeFrequency(f, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/transfers.txt").c_str()); @@ -349,12 +318,10 @@ bool Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeTransfer(t, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str()); @@ -372,12 +339,10 @@ bool Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeFare(f, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/fare_rules.txt").c_str()); @@ -395,12 +360,10 @@ bool Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeFareRule(f, &csvw); } fs.close(); - - return true; } // ____________________________________________________________________________ -bool Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/shapes.txt").c_str()); @@ -439,8 +402,6 @@ bool Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const { } fs.close(); - - return true; } // ____________________________________________________________________________ @@ -460,7 +421,7 @@ bool Writer::writeTrips(gtfs::Feed* sourceFeed, std::ostream* os) const { } // ____________________________________________________________________________ -bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const { +void Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const { std::ifstream fs; fs.open((sourceFeed->getPath() + "/stop_times.txt").c_str()); @@ -491,8 +452,6 @@ bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const { w.writeStopTime(st, &csvw); } fs.close(); - - return true; } // ___________________________________________________________________________ @@ -508,4 +467,3 @@ void Writer::cannotWrite(const std::string& file, const std::string& file2) { ss << "(temporary file for " << file2 << ") Could not write to file"; throw ad::cppgtfs::WriterException(ss.str(), file); } - diff --git a/src/pfaedle/gtfs/Writer.h b/src/pfaedle/gtfs/Writer.h index 917195d..9b894c9 100644 --- a/src/pfaedle/gtfs/Writer.h +++ b/src/pfaedle/gtfs/Writer.h @@ -16,22 +16,22 @@ class Writer { public: Writer() {} - bool write(Feed* sourceFeed, const std::string& path) const; + void write(Feed* sourceFeed, const std::string& path) const; private: - bool writeFeedInfo(Feed* f, std::ostream* os) const; - bool writeAgency(Feed* f, std::ostream* os) const; - bool writeStops(Feed* f, std::ostream* os) const; - bool writeRoutes(Feed* f, std::ostream* os) const; - bool writeCalendar(Feed* f, std::ostream* os) const; - bool writeCalendarDates(Feed* f, std::ostream* os) const; - bool writeFrequencies(Feed* f, std::ostream* os) const; - bool writeTransfers(Feed* f, std::ostream* os) const; - bool writeFares(Feed* f, std::ostream* os) const; - bool writeFareRules(Feed* f, std::ostream* os) const; - bool writeShapes(Feed* f, std::ostream* os) const; + void writeFeedInfo(Feed* f, std::ostream* os) const; + void writeAgency(Feed* f, std::ostream* os) const; + void writeStops(Feed* f, std::ostream* os) const; + void writeRoutes(Feed* f, std::ostream* os) const; + void writeCalendar(Feed* f, std::ostream* os) const; + void writeCalendarDates(Feed* f, std::ostream* os) const; + void writeFrequencies(Feed* f, std::ostream* os) const; + void writeTransfers(Feed* f, std::ostream* os) const; + void writeFares(Feed* f, std::ostream* os) const; + void writeFareRules(Feed* f, std::ostream* os) const; + void writeShapes(Feed* f, std::ostream* os) const; bool writeTrips(Feed* f, std::ostream* os) const; - bool writeStopTimes(Feed* f, std::ostream* os) const; + void writeStopTimes(Feed* f, std::ostream* os) const; static void cannotWrite(const std::string& file, const std::string& file2); static void cannotWrite(const std::string& file); diff --git a/src/pfaedle/netgraph/EdgePL.h b/src/pfaedle/netgraph/EdgePL.h index 58b767f..c6803ea 100644 --- a/src/pfaedle/netgraph/EdgePL.h +++ b/src/pfaedle/netgraph/EdgePL.h @@ -26,7 +26,7 @@ namespace netgraph { class EdgePL { public: EdgePL() {} - EdgePL(const LINE& l, const std::set& trips) + EdgePL(const LINE& l, const std::vector& trips) : _l(l), _trips(trips) { for (const auto t : _trips) { _routeShortNames.insert(t->getRoute()->getShortName()); @@ -46,7 +46,7 @@ class EdgePL { private: LINE _l; - std::set _trips; + std::vector _trips; std::set _routeShortNames; std::set _tripShortNames; }; diff --git a/src/pfaedle/osm/BBoxIdx.cpp b/src/pfaedle/osm/BBoxIdx.cpp index c25d378..c841d47 100644 --- a/src/pfaedle/osm/BBoxIdx.cpp +++ b/src/pfaedle/osm/BBoxIdx.cpp @@ -31,10 +31,10 @@ bool BBoxIdx::contains(const Point& p) const { // _____________________________________________________________________________ BOX BBoxIdx::getFullWebMercBox() const { return BOX( - util::geo::latLngToWebMerc( - _root.box.getLowerLeft().getY(), _root.box.getLowerLeft().getX()), - util::geo::latLngToWebMerc( - _root.box.getUpperRight().getY(), _root.box.getUpperRight().getX())); + util::geo::latLngToWebMerc(_root.box.getLowerLeft().getY(), + _root.box.getLowerLeft().getX()), + util::geo::latLngToWebMerc(_root.box.getUpperRight().getY(), + _root.box.getUpperRight().getX())); } // _____________________________________________________________________________ diff --git a/src/pfaedle/osm/OsmBuilder.cpp b/src/pfaedle/osm/OsmBuilder.cpp index 3da48df..6b9e488 100644 --- a/src/pfaedle/osm/OsmBuilder.cpp +++ b/src/pfaedle/osm/OsmBuilder.cpp @@ -18,7 +18,6 @@ #include "pfaedle/osm/OsmBuilder.h" #include "pfaedle/osm/OsmFilter.h" #include "pfaedle/osm/Restrictor.h" -#include "pfaedle/trgraph/StatGroup.h" #include "util/Misc.h" #include "util/Nullable.h" #include "util/log/Log.h" @@ -40,20 +39,14 @@ using pfaedle::trgraph::Graph; using pfaedle::trgraph::Node; using pfaedle::trgraph::NodePL; using pfaedle::trgraph::Normalizer; -using pfaedle::trgraph::StatGroup; using pfaedle::trgraph::StatInfo; using pfaedle::trgraph::TransitEdgeLine; using util::Nullable; using util::geo::Box; -using util::geo::webMercMeterDist; +using util::geo::M_PER_DEG; // _____________________________________________________________________________ bool EqSearch::operator()(const Node* cand, const StatInfo* si) const { - if (orphanSnap && cand->pl().getSI() && - (!cand->pl().getSI()->getGroup() || - cand->pl().getSI()->getGroup()->getStops().size() == 0)) { - return true; - } return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi; } @@ -62,8 +55,8 @@ OsmBuilder::OsmBuilder() {} // _____________________________________________________________________________ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, - Graph* g, const BBoxIdx& bbox, size_t gridSize, - router::FeedStops* fs, Restrictor* res) { + Graph* g, const BBoxIdx& bbox, double gridSize, + Restrictor* res) { if (!bbox.size()) return; LOG(INFO) << "Reading OSM file " << path << " ... "; @@ -101,85 +94,93 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts, // * match the filter criteria // * have been used in a way in pass 3 - LOG(VDEBUG) << "Reading bounding box nodes..."; + LOG(DEBUG) << "Reading bounding box nodes..."; skipUntil(&xml, "node"); pfxml::parser_state nodeBeg = xml.state(); pfxml::parser_state edgesBeg = readBBoxNds(&xml, &bboxNodes, &noHupNodes, filter, bbox); - LOG(VDEBUG) << "Reading relations..."; + LOG(DEBUG) << "Reading relations..."; skipUntil(&xml, "relation"); readRels(&xml, &intmRels, &nodeRels, &wayRels, filter, attrKeys[2], &rawRests); - LOG(VDEBUG) << "Reading edges..."; + LOG(DEBUG) << "Reading edges..."; xml.set_state(edgesBeg); readEdges(&xml, g, intmRels, wayRels, filter, bboxNodes, &nodes, &multNodes, noHupNodes, attrKeys[1], rawRests, res, intmRels.flat, &eTracks, opts); - LOG(VDEBUG) << "Reading kept nodes..."; + LOG(DEBUG) << "Reading kept nodes..."; xml.set_state(nodeBeg); readNodes(&xml, g, intmRels, nodeRels, filter, bboxNodes, &nodes, &multNodes, &orphanStations, attrKeys[0], intmRels.flat, opts); } - LOG(VDEBUG) << "OSM ID set lookups: " << osm::OsmIdSet::LOOKUPS - << ", file lookups: " << osm::OsmIdSet::FLOOKUPS; + LOG(DEBUG) << "OSM ID set lookups: " << osm::OsmIdSet::LOOKUPS + << ", file lookups: " << osm::OsmIdSet::FLOOKUPS; - LOG(VDEBUG) << "Applying edge track numbers..."; + LOG(DEBUG) << "Applying edge track numbers..."; writeEdgeTracks(eTracks); eTracks.clear(); { - LOG(VDEBUG) << "Fixing gaps..."; - NodeGrid ng = buildNodeIdx(g, gridSize, bbox.getFullWebMercBox(), false); + LOG(DEBUG) << "Fixing gaps..."; + NodeGrid ng = buildNodeIdx(g, gridSize, bbox.getFullBox(), false); + LOG(DEBUG) << "Grid size of " << ng.getXWidth() << "x" << ng.getYHeight(); fixGaps(g, &ng); } - LOG(VDEBUG) << "Writing edge geoms..."; - writeGeoms(g); + LOG(DEBUG) << "Snapping stations..."; + snapStats(opts, g, bbox, gridSize, res, orphanStations); - LOG(VDEBUG) << "Snapping stations..."; - snapStats(opts, g, bbox, gridSize, fs, res, orphanStations); - - LOG(VDEBUG) << "Deleting orphan nodes..."; - deleteOrphNds(g); - - LOG(VDEBUG) << "Deleting orphan edges..."; + LOG(DEBUG) << "Deleting orphan edges..."; deleteOrphEdgs(g, opts); - LOG(VDEBUG) << "Collapsing edges..."; + LOG(DEBUG) << "Collapsing edges..."; collapseEdges(g); - LOG(VDEBUG) << "Deleting orphan nodes..."; - deleteOrphNds(g); + LOG(DEBUG) << "Writing edge geoms..."; + writeGeoms(g, opts); - LOG(VDEBUG) << "Deleting orphan edges..."; + LOG(DEBUG) << "Deleting orphan edges..."; deleteOrphEdgs(g, opts); - LOG(VDEBUG) << "Writing graph components..."; + LOG(DEBUG) << "Deleting orphan nodes..."; + deleteOrphNds(g, opts); + + LOG(DEBUG) << "Writing graph components..."; // the restrictor is needed here to prevent connections in the graph // which are not possible in reality - uint32_t comps = writeComps(g); + uint32_t comps = writeComps(g, opts); - LOG(VDEBUG) << "Simplifying geometries..."; + LOG(DEBUG) << "Simplifying geometries..."; simplifyGeoms(g); - LOG(VDEBUG) << "Writing other-direction edges..."; + LOG(DEBUG) << "Writing other-direction edges..."; writeODirEdgs(g, res); - LOG(VDEBUG) << "Write dummy node self-edges..."; + LOG(DEBUG) << "Write wrong-direction costs..."; + writeOneWayPens(g, opts); + + if (opts.noLinesPunishFact != 1.0) { + LOG(DEBUG) << "Write no-line pens..."; + writeNoLinePens(g, opts); + } + + LOG(DEBUG) << "Write dummy node self-edges..."; writeSelfEdgs(g); size_t numEdges = 0; - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { numEdges += n->getAdjListOut().size(); } - LOG(DEBUG) << "Graph has " << g->getNds()->size() << " nodes, " << numEdges - << " edges and " << comps << " connected component(s)"; + LOG(DEBUG) << "Graph has " << g->getNds().size() << " nodes, " << numEdges + << " edges and " << comps + << " connected component(s) with more than 1 node"; + LOG(DEBUG) << _lines.size() << " transit lines have been read."; } // _____________________________________________________________________________ @@ -423,10 +424,9 @@ void OsmBuilder::readWriteWays(pfxml::file* i, util::xml::XmlWriter* o, // _____________________________________________________________________________ NodePL OsmBuilder::plFromGtfs(const Stop* s, const OsmReadOpts& ops) { - NodePL ret( - util::geo::latLngToWebMerc(s->getLat(), s->getLng()), - StatInfo(ops.statNormzer.norm(s->getName()), - ops.trackNormzer.norm(s->getPlatformCode()), false)); + NodePL ret({s->getLat(), s->getLng()}, + StatInfo(ops.statNormzer.norm(s->getName()), + ops.trackNormzer.norm(s->getPlatformCode()))); #ifdef PFAEDLE_STATION_IDS // debug feature, store station id from GTFS @@ -454,8 +454,7 @@ pfxml::parser_state OsmBuilder::readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, if (inNodeBlock && xml->level() == 3 && curId && strcmp(cur.name, "tag") == 0) { - if (filter.nohup(cur.attrs.find("k")->second, - cur.attrs.find("v")->second)) { + if (filter.nohup(cur.attr("k"), cur.attr("v"))) { nohupNodes->add(curId); } } @@ -466,12 +465,15 @@ pfxml::parser_state OsmBuilder::readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, if (inNodeBlock) { // block ended if (strcmp(cur.name, "node")) return xml->state(); - double y = util::atof(cur.attrs.find("lat")->second, 7); - double x = util::atof(cur.attrs.find("lon")->second, 7); + double y = util::atof(cur.attr("lat"), 7); + double x = util::atof(cur.attr("lon"), 7); + + curId = util::atoul(cur.attr("id")); if (bbox.contains(Point(x, y))) { - curId = util::atoul(cur.attrs.find("id")->second); nodes->add(curId); + } else { + nodes->nadd(curId); } } } while (xml->next()); @@ -489,16 +491,16 @@ OsmWay OsmBuilder::nextWayWithId(pfxml::file* xml, osmid wid, if (xml->level() == 2 || xml->level() == 0) { if (w.id || strcmp(cur.name, "way")) return w; - osmid id = util::atoul(cur.attrs.find("id")->second); + osmid id = util::atoul(cur.attr("id")); if (id == wid) w.id = id; } if (w.id && xml->level() == 3) { if (strcmp(cur.name, "nd") == 0) { - w.nodes.push_back(util::atoul(cur.attrs.find("ref")->second)); + w.nodes.push_back(util::atoul(cur.attr("ref"))); } else if (strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - w.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + w.attrs[cur.attr("k")] = cur.attr("v"); } } } while (xml->next()); @@ -542,18 +544,18 @@ OsmWay OsmBuilder::nextWay(pfxml::file* xml, const RelMap& wayRels, if (keepWay(w, wayRels, filter, bBoxNodes, fl)) return w; if (strcmp(cur.name, "way")) return OsmWay(); - w.id = util::atoul(cur.attrs.find("id")->second); + w.id = util::atoul(cur.attr("id")); w.nodes.clear(); w.attrs.clear(); } if (w.id && xml->level() == 3) { if (strcmp(cur.name, "nd") == 0) { - osmid nid = util::atoul(cur.attrs.find("ref")->second); + osmid nid = util::atoul(cur.attr("ref")); w.nodes.push_back(nid); } else if (strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - w.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + w.attrs[cur.attr("k")] = cur.attr("v"); } } } while (xml->next()); @@ -626,6 +628,7 @@ void OsmBuilder::readEdges(pfxml::file* xml, Graph* g, const RelLst& rels, } else { n = (*nodes)[nid]; } + if (last) { auto e = g->addEdg(last, n, EdgePL()); if (!e) continue; @@ -692,14 +695,14 @@ OsmNode OsmBuilder::nextNode(pfxml::file* xml, NIdMap* nodes, if (strcmp(cur.name, "node")) return OsmNode(); n.attrs.clear(); - n.lat = util::atof(cur.attrs.find("lat")->second, 7); - n.lng = util::atof(cur.attrs.find("lon")->second, 7); - n.id = util::atoul(cur.attrs.find("id")->second); + n.lat = util::atof(cur.attr("lat"), 7); + n.lng = util::atof(cur.attr("lon"), 7); + n.id = util::atoul(cur.attr("id")); } if (xml->level() == 3 && n.id && strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - n.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + n.attrs[cur.attr("k")] = cur.attr("v"); } } while (xml->next()); @@ -755,44 +758,42 @@ void OsmBuilder::readNodes(pfxml::file* xml, Graph* g, const RelLst& rels, NIdMultMap* multNodes, NodeSet* orphanStations, const AttrKeySet& keepAttrs, const FlatRels& fl, const OsmReadOpts& opts) const { - StAttrGroups attrGroups; - OsmNode nd; while ((nd = nextNode(xml, nodes, multNodes, nodeRels, filter, bBoxNodes, keepAttrs, fl)) .id) { Node* n = 0; - auto pos = util::geo::latLngToWebMerc(nd.lat, nd.lng); + POINT pos = {nd.lng, nd.lat}; if (nodes->count(nd.id)) { n = (*nodes)[nd.id]; n->pl().setGeom(pos); if (filter.station(nd.attrs)) { - auto si = getStatInfo(n, nd.id, pos, nd.attrs, &attrGroups, nodeRels, - rels, opts); + auto si = getStatInfo(nd.id, nd.attrs, nodeRels, rels, opts); if (!si.isNull()) n->pl().setSI(si); } else if (filter.blocker(nd.attrs)) { n->pl().setBlocker(); + } else if (filter.turnCycle(nd.attrs)) { + n->pl().setTurnCycle(); } } else if ((*multNodes).count(nd.id)) { for (auto* n : (*multNodes)[nd.id]) { n->pl().setGeom(pos); if (filter.station(nd.attrs)) { - auto si = getStatInfo(n, nd.id, pos, nd.attrs, &attrGroups, nodeRels, - rels, opts); + auto si = getStatInfo(nd.id, nd.attrs, nodeRels, rels, opts); if (!si.isNull()) n->pl().setSI(si); } else if (filter.blocker(nd.attrs)) { n->pl().setBlocker(); + } else if (filter.turnCycle(nd.attrs)) { + n->pl().setTurnCycle(); } } } else { // these are nodes without any connected edges if (filter.station(nd.attrs)) { auto tmp = g->addNd(NodePL(pos)); - auto si = getStatInfo(tmp, nd.id, pos, nd.attrs, &attrGroups, nodeRels, - rels, opts); + auto si = getStatInfo(nd.id, nd.attrs, nodeRels, rels, opts); if (!si.isNull()) tmp->pl().setSI(si); if (tmp->pl().getSI()) { - tmp->pl().getSI()->setIsFromOsm(false); orphanStations->insert(tmp); } } @@ -828,34 +829,34 @@ OsmRel OsmBuilder::nextRel(pfxml::file* xml, const OsmFilter& filter, rel.wayRoles.clear(); rel.keepFlags = 0; rel.dropFlags = 0; - rel.id = util::atoul(cur.attrs.find("id")->second); + rel.id = util::atoul(cur.attr("id")); } if (xml->level() == 3 && rel.id) { if (strcmp(cur.name, "member") == 0) { - if (strcmp(cur.attrs.find("type")->second, "node") == 0) { - osmid id = util::atoul(cur.attrs.find("ref")->second); + if (strcmp(cur.attr("type"), "node") == 0) { + osmid id = util::atoul(cur.attr("ref")); // TODO(patrick): no need to push IDs that have been filtered out by // the bounding box!!!! rel.nodes.push_back(id); - if (cur.attrs.count("role")) { - rel.nodeRoles.push_back(cur.attrs.find("role")->second); + if (cur.attr("role")) { + rel.nodeRoles.push_back(cur.attr("role")); } else { rel.nodeRoles.push_back(""); } } - if (strcmp(cur.attrs.find("type")->second, "way") == 0) { - osmid id = util::atoul(cur.attrs.find("ref")->second); + if (strcmp(cur.attr("type"), "way") == 0) { + osmid id = util::atoul(cur.attr("ref")); rel.ways.push_back(id); - if (cur.attrs.count("role")) { - rel.wayRoles.push_back(cur.attrs.find("role")->second); + if (cur.attr("role")) { + rel.wayRoles.push_back(cur.attr("role")); } else { rel.wayRoles.push_back(""); } } } else if (strcmp(cur.name, "tag") == 0) { - if (keepAttrs.count(cur.attrs.find("k")->second)) - rel.attrs[cur.attrs.find("k")->second] = cur.attrs.find("v")->second; + if (keepAttrs.count(cur.attr("k"))) + rel.attrs[cur.attr("k")] = cur.attr("v"); } } } while (xml->next()); @@ -936,14 +937,13 @@ void OsmBuilder::readRestr(const OsmRel& rel, Restrictions* rests, // _____________________________________________________________________________ std::string OsmBuilder::getAttrByFirstMatch(const DeepAttrLst& rule, osmid id, - const AttrMap& attrs, + const AttrMap& am, const RelMap& entRels, const RelLst& rels, const Normalizer& normzer) const { std::string ret; for (const auto& s : rule) { - ret = - normzer.norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); + ret = normzer.norm(pfxml::file::decode(getAttr(s, id, am, entRels, rels))); if (!ret.empty()) return ret; } @@ -952,13 +952,12 @@ std::string OsmBuilder::getAttrByFirstMatch(const DeepAttrLst& rule, osmid id, // _____________________________________________________________________________ std::vector OsmBuilder::getAttrMatchRanked( - const DeepAttrLst& rule, osmid id, const AttrMap& attrs, - const RelMap& entRels, const RelLst& rels, - const Normalizer& normzer) const { + const DeepAttrLst& rule, osmid id, const AttrMap& am, const RelMap& entRels, + const RelLst& rels, const Normalizer& norm) const { std::vector ret; for (const auto& s : rule) { std::string tmp = - normzer.norm(pfxml::file::decode(getAttr(s, id, attrs, entRels, rels))); + norm.norm(pfxml::file::decode(getAttr(s, id, am, entRels, rels))); if (!tmp.empty()) ret.push_back(tmp); } @@ -967,11 +966,11 @@ std::vector OsmBuilder::getAttrMatchRanked( // _____________________________________________________________________________ std::string OsmBuilder::getAttr(const DeepAttrRule& s, osmid id, - const AttrMap& attrs, const RelMap& entRels, + const AttrMap& am, const RelMap& entRels, const RelLst& rels) const { if (s.relRule.kv.first.empty()) { - if (attrs.find(s.attr) != attrs.end()) { - return attrs.find(s.attr)->second; + if (am.find(s.attr) != am.end()) { + return am.find(s.attr)->second; } } else { if (entRels.count(id)) { @@ -988,9 +987,7 @@ std::string OsmBuilder::getAttr(const DeepAttrRule& s, osmid id, } // _____________________________________________________________________________ -Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, - const POINT& pos, const AttrMap& m, - StAttrGroups* groups, +Nullable OsmBuilder::getStatInfo(osmid nid, const AttrMap& m, const RelMap& nodeRels, const RelLst& rels, const OsmReadOpts& ops) const { @@ -1004,7 +1001,7 @@ Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, if (!names.size()) return Nullable(); - auto ret = StatInfo(names[0], platform, true); + auto ret = StatInfo(names[0], platform); #ifdef PFAEDLE_STATION_IDS ret.setId(getAttrByFirstMatch(ops.statAttrRules.idRule, nid, m, nodeRels, @@ -1013,62 +1010,25 @@ Nullable OsmBuilder::getStatInfo(Node* node, osmid nid, for (size_t i = 1; i < names.size(); i++) ret.addAltName(names[i]); - bool groupFound = false; - - for (const auto& rule : ops.statGroupNAttrRules) { - if (groupFound) break; - std::string ruleVal = getAttr(rule.attr, nid, m, nodeRels, rels); - if (!ruleVal.empty()) { - // check if a matching group exists - for (auto* group : (*groups)[rule.attr.attr][ruleVal]) { - if (groupFound) break; - for (const auto* member : group->getNodes()) { - if (webMercMeterDist(*member->pl().getGeom(), pos) <= rule.maxDist) { - // ok, group is matching - groupFound = true; - if (node) group->addNode(node); - ret.setGroup(group); - break; - } - } - } - } - } - - if (!groupFound) { - for (const auto& rule : ops.statGroupNAttrRules) { - std::string ruleVal = getAttr(rule.attr, nid, m, nodeRels, rels); - if (!ruleVal.empty()) { - // add new group - StatGroup* g = new StatGroup(); - if (node) g->addNode(node); - ret.setGroup(g); - (*groups)[rule.attr.attr][ruleVal].push_back(g); - break; - } - } - } - return ret; } // _____________________________________________________________________________ double OsmBuilder::dist(const Node* a, const Node* b) { - return webMercMeterDist(*a->pl().getGeom(), *b->pl().getGeom()); + return util::geo::haversine(*(a->pl().getGeom()), *(b->pl().getGeom())); } // _____________________________________________________________________________ -double OsmBuilder::webMercDist(const Node* a, const Node* b) { - return webMercMeterDist(*a->pl().getGeom(), *b->pl().getGeom()); -} - -// _____________________________________________________________________________ -void OsmBuilder::writeGeoms(Graph* g) { - for (auto* n : *g->getNds()) { +void OsmBuilder::writeGeoms(Graph* g, const OsmReadOpts& opts) { + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { - e->pl().addPoint(*e->getFrom()->pl().getGeom()); - e->pl().setLength(dist(e->getFrom(), e->getTo())); - e->pl().addPoint(*e->getTo()->pl().getGeom()); + if (!e->pl().getGeom()) { + e->pl().addPoint(*e->getFrom()->pl().getGeom()); + e->pl().addPoint(*e->getTo()->pl().getGeom()); + } + + e->pl().setCost(costToInt(dist(e->getFrom(), e->getTo()) / + opts.levelDefSpeed[e->pl().lvl()])); } } } @@ -1076,18 +1036,18 @@ void OsmBuilder::writeGeoms(Graph* g) { // _____________________________________________________________________________ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { double METER = 1; - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { if (n->getInDeg() + n->getOutDeg() == 1) { // get all nodes in distance std::set ret; - double distor = util::geo::webMercDistFactor(*n->pl().getGeom()); + double distor = util::geo::latLngDistFactor(*n->pl().getGeom()); ng->get(util::geo::pad(util::geo::getBoundingBox(*n->pl().getGeom()), - METER / distor), + (METER / M_PER_DEG) / distor), &ret); for (auto* nb : ret) { if (nb != n && (nb->getInDeg() + nb->getOutDeg()) == 1 && - webMercDist(nb, n) <= METER / distor) { - // special case: both node are non-stations, move + dist(nb, n) <= METER) { + // special case: both nodes are non-stations, move // the end point nb to n and delete nb if (!nb->pl().getSI() && !n->pl().getSI()) { Node* otherN; @@ -1095,9 +1055,6 @@ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { otherN = (*nb->getAdjListOut().begin())->getOtherNd(nb); else otherN = (*nb->getAdjListIn().begin())->getOtherNd(nb); - LINE l; - l.push_back(*otherN->pl().getGeom()); - l.push_back(*n->pl().getGeom()); Edge* e; if (nb->getOutDeg()) @@ -1105,7 +1062,6 @@ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { else e = g->addEdg(otherN, n, (*nb->getAdjListIn().begin())->pl()); if (e) { - *e->pl().getGeom() = l; g->delNd(nb); ng->remove(nb); } @@ -1123,25 +1079,27 @@ void OsmBuilder::fixGaps(Graph* g, NodeGrid* ng) { } // _____________________________________________________________________________ -EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, size_t size, - const BOX& webMercBox) { - EdgeGrid ret(size, size, webMercBox, false); - for (auto* n : *g->getNds()) { +EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, double size, const BOX& box) { + EdgeGrid ret(size, size, box, false); + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { - assert(e->pl().getGeom()); - ret.add(*e->pl().getGeom(), e); + auto llGeom = + LINE{*e->getFrom()->pl().getGeom(), *e->getTo()->pl().getGeom()}; + ret.add(llGeom, e); } } return ret; } // _____________________________________________________________________________ -NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, +NodeGrid OsmBuilder::buildNodeIdx(Graph* g, double size, const BOX& box, bool which) { - NodeGrid ret(size, size, webMercBox, false); - for (auto* n : *g->getNds()) { + NodeGrid ret(size, size, box, false); + for (auto* n : g->getNds()) { + // only orphan nodes if (!which && n->getInDeg() + n->getOutDeg() == 1) ret.add(*n->pl().getGeom(), n); + // only station nodes else if (which && n->pl().getSI()) ret.add(*n->pl().getGeom(), n); } @@ -1153,8 +1111,8 @@ Node* OsmBuilder::depthSearch(const Edge* e, const StatInfo* si, const POINT& p, double maxD, int maxFullTurns, double minAngle, const SearchFunc& sfunc) { // shortcuts - double dFrom = webMercMeterDist(*e->getFrom()->pl().getGeom(), p); - double dTo = webMercMeterDist(*e->getTo()->pl().getGeom(), p); + double dFrom = haversine(*e->getFrom()->pl().getGeom(), p); + double dTo = haversine(*e->getTo()->pl().getGeom(), p); if (dFrom > maxD && dTo > maxD) return 0; if (dFrom <= maxD && sfunc(e->getFrom(), si)) return e->getFrom(); @@ -1197,13 +1155,15 @@ Node* OsmBuilder::depthSearch(const Edge* e, const StatInfo* si, const POINT& p, if (util::geo::innerProd(nodeP, fromP, toP) < minAngle) fullTurn = 1; } + double eLen = dist(edg->getFrom(), edg->getTo()); + if ((maxFullTurns < 0 || cur.fullTurns + fullTurn <= maxFullTurns) && - cur.dist + edg->pl().getLength() < maxD && !closed.count(cand)) { + cur.dist + eLen < maxD && !closed.count(cand)) { if (sfunc(cand, si)) { return cand; } else { - pq.push(NodeCand{cur.dist + edg->pl().getLength(), cand, edg, - cur.fullTurns + fullTurn}); + pq.push( + NodeCand{cur.dist + eLen, cand, edg, cur.fullTurns + fullTurn}); } } } @@ -1220,96 +1180,39 @@ bool OsmBuilder::isBlocked(const Edge* e, const StatInfo* si, const POINT& p, // _____________________________________________________________________________ Node* OsmBuilder::eqStatReach(const Edge* e, const StatInfo* si, const POINT& p, - double maxD, int maxFullTurns, double minAngle, - bool orphanSnap) { - return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, - EqSearch(orphanSnap)); + double maxD, int maxFullTurns, double minAngle) { + return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, EqSearch()); } // _____________________________________________________________________________ void OsmBuilder::getEdgCands(const POINT& geom, EdgeCandPQ* ret, EdgeGrid* eg, double d) { - double distor = util::geo::webMercDistFactor(geom); + double distor = util::geo::latLngDistFactor(geom); std::set neighs; - BOX box = util::geo::pad(util::geo::getBoundingBox(geom), d / distor); + BOX box = + util::geo::pad(util::geo::getBoundingBox(geom), (d / M_PER_DEG) / distor); eg->get(box, &neighs); for (auto* e : neighs) { double dist = util::geo::distToSegment(*e->getFrom()->pl().getGeom(), *e->getTo()->pl().getGeom(), geom); - if (dist * distor <= d) { + if (dist * distor * M_PER_DEG <= d) { ret->push(EdgeCand(-dist, e)); } } } // _____________________________________________________________________________ -std::set OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng, - double d) { - std::set ret; - double distor = util::geo::webMercDistFactor(*s.getGeom()); - std::set neighs; - BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor); - ng->get(box, &neighs); - - for (auto* n : neighs) { - if (n->pl().getSI() && n->pl().getSI()->simi(s.getSI()) > 0.5) { - double dist = webMercMeterDist(*n->pl().getGeom(), *s.getGeom()); - if (dist < d) ret.insert(n); - } - } - - return ret; -} - -// _____________________________________________________________________________ -Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) { - double distor = util::geo::webMercDistFactor(*s.getGeom()); - std::set neighs; - BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor); - ng->get(box, &neighs); - - Node* ret = 0; - double bestD = std::numeric_limits::max(); - - for (auto* n : neighs) { - if (n->pl().getSI() && n->pl().getSI()->simi(s.getSI()) > 0.5) { - double dist = webMercMeterDist(*n->pl().getGeom(), *s.getGeom()); - if (dist < d && dist < bestD) { - bestD = dist; - ret = n; - } - } - } - - return ret; -} - -// _____________________________________________________________________________ -std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, - NodeGrid* sng, const OsmReadOpts& opts, - Restrictor* restor, bool surrHeur, - bool orphSnap, double d) { +void OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, + const OsmReadOpts& opts, Restrictor* restor, + double d) { assert(s->getSI()); - std::set ret; EdgeCandPQ pq; getEdgCands(*s->getGeom(), &pq, eg, d); - if (pq.empty() && surrHeur) { - // no station found in the first round, try again with the nearest - // surrounding station with matching name - const Node* best = getMatchingNd(*s, sng, opts.maxSnapFallbackHeurDistance); - if (best) { - getEdgCands(*best->pl().getGeom(), &pq, eg, d); - } else { - // if still no luck, get edge cands in fallback snap distance - getEdgCands(*s->getGeom(), &pq, eg, opts.maxSnapFallbackHeurDistance); - } - } - while (!pq.empty()) { auto* e = pq.top().second; pq.pop(); @@ -1319,96 +1222,51 @@ std::set OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, Node* eq = 0; if (!(eq = eqStatReach(e, s->getSI(), geom, 2 * d, 0, - opts.maxAngleSnapReach, orphSnap))) { + opts.maxAngleSnapReach))) { if (e->pl().lvl() > opts.maxSnapLevel) continue; if (isBlocked(e, s->getSI(), geom, opts.maxBlockDistance, 0, opts.maxAngleSnapReach)) { continue; } - // if the projected position is near (< 2 meters) the end point of this + // if the projected position is near (< 0.5 meters) the end point of this // way and the endpoint is not already a station, place the station there. if (!e->getFrom()->pl().getSI() && - webMercMeterDist(geom, *e->getFrom()->pl().getGeom()) < 2) { + haversine(geom, *e->getFrom()->pl().getGeom()) < .5) { e->getFrom()->pl().setSI(*s->getSI()); - if (s->getSI()->getGroup()) - s->getSI()->getGroup()->addNode(e->getFrom()); - ret.insert(e->getFrom()); } else if (!e->getTo()->pl().getSI() && - webMercMeterDist(geom, *e->getTo()->pl().getGeom()) < 2) { + haversine(geom, *e->getTo()->pl().getGeom()) < .5) { e->getTo()->pl().setSI(*s->getSI()); - if (s->getSI()->getGroup()) s->getSI()->getGroup()->addNode(e->getTo()); - ret.insert(e->getTo()); } else { s->setGeom(geom); Node* n = g->addNd(*s); - if (n->pl().getSI()->getGroup()) - n->pl().getSI()->getGroup()->addNode(n); sng->add(geom, n); auto ne = g->addEdg(e->getFrom(), n, e->pl()); - ne->pl().setLength(webMercDist(n, e->getFrom())); - LINE l; - l.push_back(*e->getFrom()->pl().getGeom()); - l.push_back(*n->pl().getGeom()); - *ne->pl().getGeom() = l; - eg->add(l, ne); + ne->pl().setCost(costToInt(dist(e->getFrom(), n) / + opts.levelDefSpeed[ne->pl().lvl()])); + eg->add({*e->getFrom()->pl().getGeom(), *n->pl().getGeom()}, ne); auto nf = g->addEdg(n, e->getTo(), e->pl()); - nf->pl().setLength(webMercDist(n, e->getTo())); - LINE ll; - ll.push_back(*n->pl().getGeom()); - ll.push_back(*e->getTo()->pl().getGeom()); - *nf->pl().getGeom() = ll; - eg->add(ll, nf); + nf->pl().setCost(costToInt(dist(n, e->getTo()) / + opts.levelDefSpeed[nf->pl().lvl()])); + eg->add({*n->pl().getGeom(), *e->getTo()->pl().getGeom()}, nf); // replace edge in restrictor restor->replaceEdge(e, ne, nf); g->delEdg(e->getFrom(), e->getTo()); eg->remove(e); - ret.insert(n); } } else { // if the snapped station is very near to the original OSM station // write additional info from this snap station to the equivalent stat - if (webMercMeterDist(*s->getGeom(), *eq->pl().getGeom()) < - opts.maxOsmStationDistance) { + if (haversine(*s->getGeom(), *eq->pl().getGeom()) < 5) { if (eq->pl().getSI()->getTrack().empty()) eq->pl().getSI()->setTrack(s->getSI()->getTrack()); } - ret.insert(eq); } } - - return ret; -} - -// _____________________________________________________________________________ -StatGroup* OsmBuilder::groupStats(const NodeSet& s) { - if (!s.size()) return 0; - // reference group - StatGroup* ret = new StatGroup(); - bool used = false; - - for (auto* n : s) { - if (!n->pl().getSI()) continue; - used = true; - if (n->pl().getSI()->getGroup()) { - // this node is already in a group - merge this group with this one - ret->merge(n->pl().getSI()->getGroup()); - } else { - ret->addNode(n); - n->pl().getSI()->setGroup(ret); - } - } - - if (!used) { - delete ret; - return 0; - } - - return ret; } // _____________________________________________________________________________ @@ -1423,6 +1281,7 @@ std::vector OsmBuilder::getLines( elp = _relLines[relId]; } else { TransitEdgeLine el; + el.color = ad::cppgtfs::gtfs::NO_COLOR; bool found = false; for (const auto& r : ops.relLinerules.sNameRule) { @@ -1460,6 +1319,23 @@ std::vector OsmBuilder::getLines( if (found) break; } + found = false; + for (const auto& r : ops.relLinerules.colorRule) { + for (const auto& relAttr : rels.rels[relId]) { + if (relAttr.first == r) { + auto dec = pfxml::file::decode(relAttr.second); + auto color = parseHexColor(dec); + if (color == ad::cppgtfs::gtfs::NO_COLOR) + color = parseHexColor(std::string("#") + dec); + if (color != ad::cppgtfs::gtfs::NO_COLOR) { + found = true; + el.color = color; + } + } + } + if (found) break; + } + if (!el.shortName.size() && !el.fromStr.size() && !el.toStr.size()) continue; @@ -1481,15 +1357,6 @@ std::vector OsmBuilder::getLines( // _____________________________________________________________________________ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, AttrKeySet sets[3]) const { - for (const auto& i : opts.statGroupNAttrRules) { - if (i.attr.relRule.kv.first.empty()) { - sets[0].insert(i.attr.attr); - } else { - sets[2].insert(i.attr.relRule.kv.first); - sets[2].insert(i.attr.attr); - } - } - for (const auto& i : opts.keepFilter) { for (size_t j = 0; j < 3; j++) sets[j].insert(i.first); } @@ -1523,6 +1390,10 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, sets[0].insert(i.first); } + for (const auto& i : opts.turnCycleFilter) { + sets[0].insert(i.first); + } + for (uint8_t j = 0; j < 7; j++) { for (const auto& kv : *(opts.levelFilters + j)) { sets[1].insert(kv.first); @@ -1550,6 +1421,8 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, opts.relLinerules.fromNameRule.end()); sets[2].insert(opts.relLinerules.sNameRule.begin(), opts.relLinerules.sNameRule.end()); + sets[2].insert(opts.relLinerules.colorRule.begin(), + opts.relLinerules.colorRule.end()); for (const auto& i : opts.statAttrRules.nameRule) { if (i.relRule.kv.first.empty()) { @@ -1588,12 +1461,26 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts, } } +// _____________________________________________________________________________ +void OsmBuilder::deleteOrphNds(Graph* g, const OsmReadOpts& opts) { + UNUSED(opts); + for (auto i = g->getNds().begin(); i != g->getNds().end();) { + if ((*i)->getInDeg() + (*i)->getOutDeg() != 0 || (*i)->pl().getSI()) { + ++i; + continue; + } + + i = g->delNd(*i); + } +} + // _____________________________________________________________________________ void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) { size_t ROUNDS = 3; for (size_t c = 0; c < ROUNDS; c++) { - for (auto i = g->getNds()->begin(); i != g->getNds()->end();) { - if ((*i)->getInDeg() + (*i)->getOutDeg() != 1 || (*i)->pl().getSI()) { + for (auto i = g->getNds().begin(); i != g->getNds().end();) { + if ((*i)->getInDeg() + (*i)->getOutDeg() != 1 || (*i)->pl().getSI() || + (*i)->pl().isTurnCycle()) { ++i; continue; } @@ -1607,21 +1494,6 @@ void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) { } i = g->delNd(*i); - continue; - i++; - } - } -} - -// _____________________________________________________________________________ -void OsmBuilder::deleteOrphNds(Graph* g) { - for (auto i = g->getNds()->begin(); i != g->getNds()->end();) { - if ((*i)->getInDeg() + (*i)->getOutDeg() == 0 && - !((*i)->pl().getSI() && (*i)->pl().getSI()->getGroup())) { - i = g->delNd(i); - // TODO(patrick): maybe delete from node grid? - } else { - i++; } } } @@ -1632,11 +1504,11 @@ bool OsmBuilder::edgesSim(const Edge* a, const Edge* b) { return false; if (a->pl().lvl() != b->pl().lvl()) return false; if (a->pl().getLines().size() != b->pl().getLines().size()) return false; - if (a->pl().getLines() != b->pl().getLines()) return false; if (a->pl().oneWay() && b->pl().oneWay()) { if (a->getFrom() != b->getTo() && a->getTo() != b->getFrom()) return false; } if (a->pl().isRestricted() || b->pl().isRestricted()) return false; + if (a->pl().getLines() != b->pl().getLines()) return false; return true; } @@ -1651,39 +1523,60 @@ const EdgePL& OsmBuilder::mergeEdgePL(Edge* a, Edge* b) { else n = a->getTo(); + if (a->pl().getGeom() == 0) { + a->pl().addPoint(*a->getFrom()->pl().getGeom()); + a->pl().addPoint(*a->getTo()->pl().getGeom()); + } + if (a->getTo() == n && b->getTo() == n) { // --> n <-- - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->rbegin(), - b->pl().getGeom()->rend()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->rbegin(), + b->pl().getGeom()->rend()); + } else { + a->pl().getGeom()->push_back(*b->getFrom()->pl().getGeom()); + } } else if (a->getTo() == n && b->getFrom() == n) { // --> n --> - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->begin(), - b->pl().getGeom()->end()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->begin(), + b->pl().getGeom()->end()); + } else { + a->pl().getGeom()->push_back(*b->getTo()->pl().getGeom()); + } } else if (a->getFrom() == n && b->getTo() == n) { // <-- n <-- std::reverse(a->pl().getGeom()->begin(), a->pl().getGeom()->end()); - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->rbegin(), - b->pl().getGeom()->rend()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->rbegin(), + b->pl().getGeom()->rend()); + } else { + a->pl().getGeom()->push_back(*b->getFrom()->pl().getGeom()); + } } else { // <-- n --> std::reverse(a->pl().getGeom()->begin(), a->pl().getGeom()->end()); - a->pl().getGeom()->insert(a->pl().getGeom()->end(), - b->pl().getGeom()->begin(), - b->pl().getGeom()->end()); + if (b->pl().getGeom()) { + a->pl().getGeom()->insert(a->pl().getGeom()->end(), + b->pl().getGeom()->begin(), + b->pl().getGeom()->end()); + } else { + a->pl().getGeom()->push_back(*b->getTo()->pl().getGeom()); + } } - a->pl().setLength(a->pl().getLength() + b->pl().getLength()); - return a->pl(); } // _____________________________________________________________________________ void OsmBuilder::collapseEdges(Graph* g) { - for (auto* n : *g->getNds()) { - if (n->getOutDeg() + n->getInDeg() != 2 || n->pl().getSI()) continue; + for (auto n : g->getNds()) { + if (n->getOutDeg() + n->getInDeg() != 2 || n->pl().getSI() || + n->pl().isTurnCycle()) + continue; Edge* ea; Edge* eb; @@ -1699,7 +1592,7 @@ void OsmBuilder::collapseEdges(Graph* g) { } // important, we don't have a multigraph! if the same edge - // will already exist, leave this node + // already exists, leave this node if (g->getEdg(ea->getOtherNd(n), eb->getOtherNd(n))) continue; if (g->getEdg(eb->getOtherNd(n), ea->getOtherNd(n))) continue; @@ -1709,6 +1602,7 @@ void OsmBuilder::collapseEdges(Graph* g) { } else { g->addEdg(ea->getOtherNd(n), eb->getOtherNd(n), mergeEdgePL(ea, eb)); } + g->delEdg(ea->getFrom(), ea->getTo()); g->delEdg(eb->getFrom(), eb->getTo()); } @@ -1717,49 +1611,55 @@ void OsmBuilder::collapseEdges(Graph* g) { // _____________________________________________________________________________ void OsmBuilder::simplifyGeoms(Graph* g) { - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { - (*e->pl().getGeom()) = util::geo::simplify(*e->pl().getGeom(), 0.5); + (*e->pl().getGeom()) = + util::geo::simplify(*e->pl().getGeom(), 0.5 / M_PER_DEG); } } } // _____________________________________________________________________________ -uint32_t OsmBuilder::writeComps(Graph* g) { - Component* comp = new Component{7}; +uint32_t OsmBuilder::writeComps(Graph* g, const OsmReadOpts& opts) { + NodePL::comps.clear(); + NodePL::comps.emplace_back(Component{0}); uint32_t numC = 0; + uint64_t numNds = 0; - for (auto* n : *g->getNds()) { - if (!n->pl().getComp()) { + double fac = opts.maxSpeedCorFac; + + for (auto* n : g->getNds()) { + if (!n->pl().getCompId()) { std::stack> q; q.push(std::pair(n, 0)); while (!q.empty()) { std::pair cur = q.top(); q.pop(); - cur.first->pl().setComp(comp); + cur.first->pl().setComp(NodePL::comps.size()); + numNds++; for (auto* e : cur.first->getAdjListOut()) { - if (e->pl().lvl() < comp->minEdgeLvl) - comp->minEdgeLvl = e->pl().lvl(); - if (!e->getOtherNd(cur.first)->pl().getComp()) + double speed = opts.levelDefSpeed[e->pl().lvl()] / fac; + if (speed > NodePL::comps.back().maxSpeed) + NodePL::comps.back().maxSpeed = speed; + if (!e->getOtherNd(cur.first)->pl().getCompId()) q.push(std::pair(e->getOtherNd(cur.first), e)); } for (auto* e : cur.first->getAdjListIn()) { - if (e->pl().lvl() < comp->minEdgeLvl) - comp->minEdgeLvl = e->pl().lvl(); - if (!e->getOtherNd(cur.first)->pl().getComp()) + double speed = opts.levelDefSpeed[e->pl().lvl()] / fac; + if (speed > NodePL::comps.back().maxSpeed) + NodePL::comps.back().maxSpeed = speed; + if (!e->getOtherNd(cur.first)->pl().getCompId()) q.push(std::pair(e->getOtherNd(cur.first), e)); } } - numC++; - comp = new Component{7}; + if (numNds > 1) numC++; + NodePL::comps.emplace_back(Component{0}); + numNds = 0; } } - // the last comp was not used - delete comp; - return numC; } @@ -1779,10 +1679,11 @@ void OsmBuilder::writeEdgeTracks(const EdgTracks& tracks) { // _____________________________________________________________________________ void OsmBuilder::writeODirEdgs(Graph* g, Restrictor* restor) { - for (auto* n : *g->getNds()) { + for (auto* n : g->getNds()) { for (auto* e : n->getAdjListOut()) { if (g->getEdg(e->getTo(), e->getFrom())) continue; auto newE = g->addEdg(e->getTo(), e->getFrom(), e->pl().revCopy()); + assert(newE->pl().getGeom()); if (e->pl().isRestricted()) restor->duplicateEdge(e, newE); } } @@ -1790,9 +1691,45 @@ void OsmBuilder::writeODirEdgs(Graph* g, Restrictor* restor) { // _____________________________________________________________________________ void OsmBuilder::writeSelfEdgs(Graph* g) { - for (auto* n : *g->getNds()) { - if (n->pl().getSI() && n->getAdjListOut().size() == 0) { - g->addEdg(n, n); + // if a station only has degree 1, there is no way to arrive at this station + // without doing a full turn (because the outgoing candidate edge is always + // the incoming edge). This is a problem at end-stations. We solve this by + // adding self-edges with infinite costs - this still allows usage as + // arrivals, does not punish bends (because the node degree is still only 2) + // and prevents the usage of the edge to circumvent turn penalties + for (auto* n : g->getNds()) { + if (n->pl().getSI() && n->getAdjListOut().size() == 1) { + auto e = g->addEdg(n, n); + e->pl().setCost(std::numeric_limits::max()); + e->pl().addPoint(*e->getFrom()->pl().getGeom()); + e->pl().addPoint(*e->getTo()->pl().getGeom()); + } + } +} + +// _____________________________________________________________________________ +void OsmBuilder::writeNoLinePens(Graph* g, const OsmReadOpts& opts) { + for (auto* n : g->getNds()) { + for (auto* e : n->getAdjListOut()) { + if (e->pl().getLines().size() == 0) { + double c = e->pl().getCost(); + c = c / 10.0; // convert into seconds + e->pl().setCost(costToInt(c * opts.noLinesPunishFact)); + } + } + } +} + +// _____________________________________________________________________________ +void OsmBuilder::writeOneWayPens(Graph* g, const OsmReadOpts& opts) { + for (auto* n : g->getNds()) { + for (auto* e : n->getAdjListOut()) { + if (e->pl().oneWay() == 2) { + double c = e->pl().getCost(); + c = c / 10.0; // convert into seconds + e->pl().setCost( + costToInt(c * opts.oneWaySpeedPen + opts.oneWayEntryCost)); + } } } } @@ -1826,12 +1763,23 @@ bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) { b = f; } - auto ap = a->pl().backHop(); - auto bp = b->pl().backHop(); - if (a->getTo() != other) ap = a->pl().frontHop(); - if (b->getTo() != other) bp = b->pl().frontHop(); + POINT ap, bp; - return router::angSmaller(ap, *other->pl().getGeom(), bp, ang); + if (a->pl().getGeom() && b->pl().getGeom()) { + ap = a->pl().backHop(); + bp = b->pl().backHop(); + if (a->getTo() != other) ap = a->pl().frontHop(); + if (b->getTo() != other) bp = b->pl().frontHop(); + } else { + assert(!a->pl().getGeom()); + assert(!b->pl().getGeom()); + ap = *a->getTo()->pl().getGeom(); + bp = *b->getTo()->pl().getGeom(); + if (a->getTo() != other) ap = *a->getFrom()->pl().getGeom(); + if (b->getTo() != other) bp = *b->getFrom()->pl().getGeom(); + } + + return util::geo::innerProd(*other->pl().getGeom(), ap, bp) > ang; } return false; @@ -1839,121 +1787,88 @@ bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) { // _____________________________________________________________________________ void OsmBuilder::snapStats(const OsmReadOpts& opts, Graph* g, - const BBoxIdx& bbox, size_t gridSize, - router::FeedStops* fs, Restrictor* res, - const NodeSet& orphanStations) { - NodeGrid sng = buildNodeIdx(g, gridSize, bbox.getFullWebMercBox(), true); - EdgeGrid eg = buildEdgeIdx(g, gridSize, bbox.getFullWebMercBox()); + const BBoxIdx& bbox, double gridSize, + Restrictor* res, const NodeSet& orphanStations) { + NodeGrid sng = buildNodeIdx(g, gridSize, bbox.getFullBox(), true); + EdgeGrid eg = buildEdgeIdx(g, gridSize, bbox.getFullBox()); LOG(DEBUG) << "Grid size of " << sng.getXWidth() << "x" << sng.getYHeight(); - for (double d : opts.maxSnapDistances) { + for (double d : opts.maxOsmStationDistances) { for (auto s : orphanStations) { - POINT geom = *s->pl().getGeom(); NodePL pl = s->pl(); - pl.getSI()->setIsFromOsm(false); - const auto& r = - snapStation(g, &pl, &eg, &sng, opts, res, false, false, d); - groupStats(r); - for (auto n : r) { - // if the snapped station is very near to the original OSM - // station, set is-from-osm to true - if (webMercMeterDist(geom, *n->pl().getGeom()) < - opts.maxOsmStationDistance) { - if (n->pl().getSI()) n->pl().getSI()->setIsFromOsm(true); - } - } - } - } - - if (!fs) return; - - std::vector notSnapped; - - for (auto& s : *fs) { - bool snapped = false; - auto pl = plFromGtfs(s.first, opts); - for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { - double d = opts.maxSnapDistances[i]; - - StatGroup* group = groupStats( - snapStation(g, &pl, &eg, &sng, opts, res, - i == opts.maxSnapDistances.size() - 1, false, d)); - - if (group) { - group->addStop(s.first); - (*fs)[s.first] = *group->getNodes().begin(); - snapped = true; - } - } - if (!snapped) { - LOG(VDEBUG) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s.first->getLat() << "," << s.first->getLng() - << ") in normal run, trying again later in orphan mode."; - if (!bbox.contains(*pl.getGeom())) { - LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() - << "' does not lie within the bounds for this graph and " - "may be a stray station"; - } - notSnapped.push_back(s.first); - } - } - - if (notSnapped.size()) - LOG(VDEBUG) << notSnapped.size() - << " stations could not be snapped in " - "normal run, trying again in orphan " - "mode."; - - // try again, but aggressively snap to orphan OSM stations which have - // not been assigned to any GTFS stop yet - for (auto& s : notSnapped) { - bool snapped = false; - auto pl = plFromGtfs(s, opts); - for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { - double d = opts.maxSnapDistances[i]; - - StatGroup* group = groupStats( - snapStation(g, &pl, &eg, &sng, opts, res, - i == opts.maxSnapDistances.size() - 1, true, d)); - - if (group) { - group->addStop(s); - // add the added station name as an alt name to ensure future - // similarity - for (auto n : group->getNodes()) { - if (n->pl().getSI()) - n->pl().getSI()->addAltName(pl.getSI()->getName()); - } - (*fs)[s] = *group->getNodes().begin(); - snapped = true; - } - } - if (!snapped) { - // finally give up - - // add a group with only this stop in it - StatGroup* dummyGroup = new StatGroup(); - Node* dummyNode = g->addNd(pl); - - dummyNode->pl().getSI()->setGroup(dummyGroup); - dummyGroup->addNode(dummyNode); - dummyGroup->addStop(s); - (*fs)[s] = dummyNode; - if (!bbox.contains(*pl.getGeom())) { - LOG(VDEBUG) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; - LOG(VDEBUG) << "Note: '" << pl.getSI()->getName() - << "' does not lie within the bounds for this graph and " - "may be a stray station"; - } else { - // only warn if it is contained in the BBOX for this graph - LOG(WARN) << "Could not snap station " - << "(" << pl.getSI()->getName() << ")" - << " (" << s->getLat() << "," << s->getLng() << ")"; - } + snapStation(g, &pl, &eg, &sng, opts, res, d); } } } + +// _____________________________________________________________________________ +uint32_t OsmBuilder::costToInt(double c) { + // always round upwards, otherwise when combined with the heuristic which + // is always rounded downwards the PQ monotonicity is not ensured anymore - + // with a downward rounding, the rounding errors may sum up so high that the + // path will get cheaper than the heuristic cost + uint32_t val = std::ceil(c * 10); + if (std::ceil(c * 10) > std::numeric_limits::max()) { + LOG(DEBUG) << "Cost " << c + << " does not fit in unsigned 32 bit integer, defaulting to " + << std::numeric_limits::max() << "."; + return std::numeric_limits::max(); + } + return val; +} + +// _____________________________________________________________________________ +uint32_t OsmBuilder::parseHexColor(std::string s) const { + // TODO(patrick): not very nice + size_t proced = 0; + std::transform(s.begin(), s.end(), s.begin(), ::toupper); + std::string ret = " "; + if (s.size() == 7 && s[0] == '#') { + for (size_t i = 1; i < 7; i++) { + if (isdigit(s[i])) + ret[i - 1] = s[i]; + else if (isalpha(s[i]) && (s[i] > 64 && s[i] < 71)) + ret[i - 1] = s[i]; + else + return ad::cppgtfs::gtfs::NO_COLOR; + } + + return std::stoul("0x" + ret, &proced, 16); + } + + if (s.size() == 4 && s[0] == '#') { + for (size_t i = 1; i < 4; i++) { + if (isdigit(s[i])) { + ret[(i - 1) * 2] = s[i]; + ret[(i - 1) * 2 + 1] = s[i]; + } else if (isalpha(s[i]) && (s[i] > 64 && s[i] < 71)) { + ret[(i - 1) * 2] = s[i]; + ret[(i - 1) * 2 + 1] = s[i]; + } else { + return ad::cppgtfs::gtfs::NO_COLOR; + } + } + return std::stoul("0x" + ret, &proced, 16); + } + + if (s == "BLACK") return 0x00000000; + if (s == "SILVER") return 0x00C0C0C0; + if (s == "GRAY") return 0x00808080; + if (s == "WHITE") return 0x00FFFFFF; + if (s == "MAROON") return 0x00800000; + if (s == "RED") return 0x00FF0000; + if (s == "PURPLE") return 0x00800080; + if (s == "FUCHSIA") return 0x00FF00FF; + if (s == "GREEN") return 0x00008000; + if (s == "LIME") return 0x0000FF00; + if (s == "OLIVE") return 0x00808000; + if (s == "YELLOW") return 0x00FFFF00; + if (s == "NAVY") return 0x00000080; + if (s == "BLUE") return 0x000000FF; + if (s == "TEAL") return 0x00008080; + if (s == "AQUA") return 0x0000FFFF; + + if (ret.empty()) return ad::cppgtfs::gtfs::NO_COLOR; + return std::stoul("0x" + ret, &proced, 16); +} diff --git a/src/pfaedle/osm/OsmBuilder.h b/src/pfaedle/osm/OsmBuilder.h index 8b4c54f..10029c4 100644 --- a/src/pfaedle/osm/OsmBuilder.h +++ b/src/pfaedle/osm/OsmBuilder.h @@ -30,20 +30,19 @@ namespace pfaedle { namespace osm { +using ad::cppgtfs::gtfs::Stop; +using pfaedle::router::NodeSet; +using pfaedle::trgraph::Component; +using pfaedle::trgraph::Edge; using pfaedle::trgraph::EdgeGrid; -using pfaedle::trgraph::NodeGrid; -using pfaedle::trgraph::Normalizer; +using pfaedle::trgraph::EdgePL; using pfaedle::trgraph::Graph; using pfaedle::trgraph::Node; +using pfaedle::trgraph::NodeGrid; using pfaedle::trgraph::NodePL; -using pfaedle::trgraph::Edge; -using pfaedle::trgraph::EdgePL; -using pfaedle::trgraph::TransitEdgeLine; +using pfaedle::trgraph::Normalizer; using pfaedle::trgraph::StatInfo; -using pfaedle::trgraph::StatGroup; -using pfaedle::trgraph::Component; -using pfaedle::router::NodeSet; -using ad::cppgtfs::gtfs::Stop; +using pfaedle::trgraph::TransitEdgeLine; using util::Nullable; struct NodeCand { @@ -58,9 +57,8 @@ struct SearchFunc { }; struct EqSearch : public SearchFunc { - explicit EqSearch(bool orphanSnap) : orphanSnap(orphanSnap) {} + EqSearch() {} double minSimi = 0.9; - bool orphanSnap; bool operator()(const Node* cand, const StatInfo* si) const; }; @@ -87,8 +85,7 @@ class OsmBuilder { // Read the OSM file at path, and write a graph to g. Only elements // inside the bounding box will be read void read(const std::string& path, const OsmReadOpts& opts, Graph* g, - const BBoxIdx& box, size_t gridSize, router::FeedStops* fs, - Restrictor* res); + const BBoxIdx& box, double gridSize, Restrictor* res); // Based on the list of options, output an overpass XML query for getting // the data needed for routing @@ -103,8 +100,8 @@ class OsmBuilder { private: pfxml::parser_state readBBoxNds(pfxml::file* xml, OsmIdSet* nodes, - OsmIdSet* noHupNodes, const OsmFilter& filter, - const BBoxIdx& bbox) const; + OsmIdSet* noHupNodes, const OsmFilter& filter, + const BBoxIdx& bbox) const; void readRels(pfxml::file* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels, const OsmFilter& filter, const AttrKeySet& keepAttrs, @@ -140,13 +137,14 @@ class OsmBuilder { Restrictor* restor, const FlatRels& flatRels, EdgTracks* etracks, const OsmReadOpts& opts); - void readEdges(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, - const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, - OsmIdList* ret, NIdMap* nodes, const FlatRels& flatRels); + void readEdges(pfxml::file* xml, const RelMap& wayRels, + const OsmFilter& filter, const OsmIdSet& bBoxNodes, + const AttrKeySet& keepAttrs, OsmIdList* ret, NIdMap* nodes, + const FlatRels& flatRels); - OsmWay nextWay(pfxml::file* xml, const RelMap& wayRels, const OsmFilter& filter, - const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs, - const FlatRels& flatRels) const; + OsmWay nextWay(pfxml::file* xml, const RelMap& wayRels, + const OsmFilter& filter, const OsmIdSet& bBoxNodes, + const AttrKeySet& keepAttrs, const FlatRels& flatRels) const; bool keepWay(const OsmWay& w, const RelMap& wayRels, const OsmFilter& filter, const OsmIdSet& bBoxNodes, const FlatRels& fl) const; @@ -168,52 +166,46 @@ class OsmBuilder { const AttrKeySet& keepAttrs) const; protected: - Nullable getStatInfo(Node* node, osmid nid, const POINT& pos, - const AttrMap& m, StAttrGroups* groups, + Nullable getStatInfo(osmid nid, const AttrMap& m, const RelMap& nodeRels, const RelLst& rels, const OsmReadOpts& ops) const; static void snapStats(const OsmReadOpts& opts, Graph* g, const BBoxIdx& bbox, - size_t gridSize, router::FeedStops* fs, Restrictor* res, + double gridSize, Restrictor* res, const NodeSet& orphanStations); - static void writeGeoms(Graph* g); - static void deleteOrphNds(Graph* g); + static void writeGeoms(Graph* g, const OsmReadOpts& opts); static void deleteOrphEdgs(Graph* g, const OsmReadOpts& opts); + static void deleteOrphNds(Graph* g, const OsmReadOpts& opts); static double dist(const Node* a, const Node* b); - static double webMercDist(const Node* a, const Node* b); - static NodeGrid buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox, + static NodeGrid buildNodeIdx(Graph* g, double size, const BOX& box, bool which); - static EdgeGrid buildEdgeIdx(Graph* g, size_t size, const BOX& webMercBox); + static EdgeGrid buildEdgeIdx(Graph* g, double size, const BOX& box); static void fixGaps(Graph* g, NodeGrid* ng); static void collapseEdges(Graph* g); static void writeODirEdgs(Graph* g, Restrictor* restor); static void writeSelfEdgs(Graph* g); + static void writeOneWayPens(Graph* g, const OsmReadOpts& opts); + static void writeNoLinePens(Graph* g, const OsmReadOpts& opts); static void writeEdgeTracks(const EdgTracks& tracks); static void simplifyGeoms(Graph* g); - static uint32_t writeComps(Graph* g); + static uint32_t writeComps(Graph* g, const OsmReadOpts& opts); static bool edgesSim(const Edge* a, const Edge* b); static const EdgePL& mergeEdgePL(Edge* a, Edge* b); static void getEdgCands(const POINT& s, EdgeCandPQ* ret, EdgeGrid* eg, double d); - static std::set getMatchingNds(const NodePL& s, NodeGrid* ng, - double d); - - static Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d); - - static NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, - const OsmReadOpts& opts, Restrictor* restor, - bool surHeur, bool orphSnap, double maxD); + static void snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, + const OsmReadOpts& opts, Restrictor* restor, + double maxD); // Checks if from the edge e, a station similar to si can be reach with less // than maxD distance and less or equal to "maxFullTurns" full turns. If // such a station exists, it is returned. If not, 0 is returned. static Node* eqStatReach(const Edge* e, const StatInfo* si, const POINT& p, - double maxD, int maxFullTurns, double maxAng, - bool orph); + double maxD, int maxFullTurns, double maxAng); static Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p, double maxD, int maxFullTurns, double minAngle, @@ -223,8 +215,6 @@ class OsmBuilder { double maxD, int maxFullTurns, double minAngle); static bool keepFullTurn(const trgraph::Node* n, double ang); - static StatGroup* groupStats(const NodeSet& s); - static NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops); std::vector getLines(const std::vector& edgeRels, @@ -254,6 +244,10 @@ class OsmBuilder { bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const; + uint32_t parseHexColor(std::string) const; + + static uint32_t costToInt(double c); + std::map _lines; std::map _relLines; }; diff --git a/src/pfaedle/osm/OsmFilter.cpp b/src/pfaedle/osm/OsmFilter.cpp index 589cf5c..d180041 100644 --- a/src/pfaedle/osm/OsmFilter.cpp +++ b/src/pfaedle/osm/OsmFilter.cpp @@ -26,6 +26,7 @@ OsmFilter::OsmFilter(const OsmReadOpts& o) _posRestr(o.restrPosRestr), _negRestr(o.restrNegRestr), _noRestr(o.noRestrFilter), + _turnCycle(o.turnCycleFilter), _levels(o.levelFilters) {} // _____________________________________________________________________________ @@ -72,6 +73,11 @@ uint64_t OsmFilter::blocker(const AttrMap& attrs) const { return contained(attrs, _blocker, NODE); } +// _____________________________________________________________________________ +uint64_t OsmFilter::turnCycle(const AttrMap& attrs) const { + return contained(attrs, _turnCycle, NODE); +} + // _____________________________________________________________________________ uint64_t OsmFilter::contained(const AttrMap& attrs, const MultAttrMap& map, Type t) { diff --git a/src/pfaedle/osm/OsmFilter.h b/src/pfaedle/osm/OsmFilter.h index daf353a..05bafc7 100644 --- a/src/pfaedle/osm/OsmFilter.h +++ b/src/pfaedle/osm/OsmFilter.h @@ -27,6 +27,7 @@ class OsmFilter { uint64_t onewayrev(const AttrMap& attrs) const; uint64_t station(const AttrMap& attrs) const; uint64_t blocker(const AttrMap& attrs) const; + uint64_t turnCycle(const AttrMap& attrs) const; uint64_t negRestr(const AttrMap& attrs) const; uint64_t posRestr(const AttrMap& attrs) const; std::vector getAttrKeys() const; @@ -46,7 +47,7 @@ class OsmFilter { private: MultAttrMap _keep, _drop, _nohup, _oneway, _onewayrev, _twoway, _station, - _blocker, _posRestr, _negRestr, _noRestr; + _blocker, _posRestr, _negRestr, _noRestr, _turnCycle; const MultAttrMap* _levels; }; } // namespace osm diff --git a/src/pfaedle/osm/OsmIdSet.cpp b/src/pfaedle/osm/OsmIdSet.cpp index 8b23bf9..3f79e71 100644 --- a/src/pfaedle/osm/OsmIdSet.cpp +++ b/src/pfaedle/osm/OsmIdSet.cpp @@ -15,6 +15,7 @@ #include #include "pfaedle/Def.h" #include "pfaedle/osm/OsmIdSet.h" +#include "util/3rdparty/MurmurHash3.h" using pfaedle::osm::OsmIdSet; @@ -28,26 +29,46 @@ OsmIdSet::OsmIdSet() _last(0), _smallest(-1), _biggest(0), + _hasInv(false), _obufpos(0), _curBlock(-1), _fsize(0) { _bitset = new std::bitset(); + _bitsetNotIn = new std::bitset(); _file = openTmpFile(); _buffer = new unsigned char[BUFFER_S]; - _outBuffer = new unsigned char[OBUFFER_S]; + _outBuffer = new unsigned char[BUFFER_S]; } // _____________________________________________________________________________ OsmIdSet::~OsmIdSet() { delete _bitset; + delete _bitsetNotIn; delete[] _buffer; if (!_closed) delete[] _outBuffer; } +// _____________________________________________________________________________ +void OsmIdSet::nadd(osmid id) { + if (_closed) throw std::exception(); + + _hasInv = true; + + uint32_t h1, h2; + MurmurHash3_x86_32(&id, 8, 469954432, &h1); + h2 = jenkins(id); + + for (int i = 0; i < 5; i++) { + uint32_t h = (h1 + i * h2) % BLOOMF_BITS; + (*_bitsetNotIn)[h] = 1; + } +} + // _____________________________________________________________________________ void OsmIdSet::add(osmid id) { if (_closed) throw std::exception(); + diskAdd(id); if (_last > id) _sorted = false; @@ -55,7 +76,14 @@ void OsmIdSet::add(osmid id) { if (id < _smallest) _smallest = id; if (id > _biggest) _biggest = id; - for (int i = 0; i < 10; i++) (*_bitset)[hash(id, i)] = 1; + uint32_t h1, h2; + MurmurHash3_x86_32(&id, 8, 469954432, &h1); + h2 = jenkins(id); + + for (int i = 0; i < 5; i++) { + uint32_t h = (h1 + i * h2) % BLOOMF_BITS; + (*_bitset)[h] = 1; + } } // _____________________________________________________________________________ @@ -69,8 +97,8 @@ void OsmIdSet::diskAdd(osmid id) { _blockEnds.push_back(id); } - if (_obufpos >= OBUFFER_S) { - ssize_t w = cwrite(_file, _outBuffer, OBUFFER_S); + if (_obufpos >= BUFFER_S) { + ssize_t w = cwrite(_file, _outBuffer, BUFFER_S); _fsize += w; _obufpos = 0; } @@ -86,7 +114,8 @@ size_t OsmIdSet::getBlock(osmid id) const { bool OsmIdSet::diskHas(osmid id) const { assert(_sorted); - if (std::find(_blockEnds.begin(), _blockEnds.end(), id) != _blockEnds.end()) { + auto a = std::lower_bound(_blockEnds.begin(), _blockEnds.end(), id); + if (a != _blockEnds.end() && *a == id) { return true; } @@ -125,12 +154,23 @@ bool OsmIdSet::has(osmid id) const { LOOKUPS++; if (!_closed) close(); + // trivial cases if (id < _smallest || id > _biggest) { return false; } - for (int i = 0; i < 10; i++) { - if ((*_bitset)[hash(id, i)] == 0) return false; + uint32_t h1, h2; + MurmurHash3_x86_32(&id, 8, 469954432, &h1); + h2 = jenkins(id); + + for (int i = 0; i < 5; i++) { + uint32_t h = (h1 + i * h2) % BLOOMF_BITS; + if ((*_bitset)[h] == 0) { + return false; + } + if (_hasInv && (*_bitsetNotIn)[h] == 0) { + return true; + } } bool has = diskHas(id); @@ -249,8 +289,8 @@ size_t OsmIdSet::cread(int f, void* buf, size_t n) const { // _____________________________________________________________________________ uint32_t OsmIdSet::knuth(uint32_t in) const { - const uint32_t prime = 2654435769; - return (in * prime) >> 2; + const uint32_t a = 2654435769; + return (in * a) >> 2; } // _____________________________________________________________________________ @@ -264,11 +304,6 @@ uint32_t OsmIdSet::jenkins(uint32_t in) const { return in >> 2; } -// _____________________________________________________________________________ -uint32_t OsmIdSet::hash(uint32_t in, int i) const { - return (knuth(in) + jenkins(in) * i) % BLOOMF_BITS; -} - // _____________________________________________________________________________ int OsmIdSet::openTmpFile() const { const std::string& fname = getTmpFName("", ""); diff --git a/src/pfaedle/osm/OsmIdSet.h b/src/pfaedle/osm/OsmIdSet.h index d19f9a1..1a16b61 100644 --- a/src/pfaedle/osm/OsmIdSet.h +++ b/src/pfaedle/osm/OsmIdSet.h @@ -25,7 +25,7 @@ static const size_t BUFFER_S = 8 * 64 * 1024; static const size_t SORT_BUFFER_S = 8 * 64 * 1024; static const size_t OBUFFER_S = 8 * 1024 * 1024; -#define BLOOMF_BITS 400000000 +#define BLOOMF_BITS 214748357 /* * A disk-based set for OSM ids. Read-access for checking the presence is @@ -39,6 +39,9 @@ class OsmIdSet { // Add an OSM id void add(osmid id); + // Add an OSM id that is NOT contained + void nadd(osmid id); + // Check if an OSM id is contained bool has(osmid id) const; @@ -57,6 +60,8 @@ class OsmIdSet { osmid _smallest; osmid _biggest; + bool _hasInv; + size_t _obufpos; mutable size_t _curBlock; mutable ssize_t _curBlockSize; @@ -64,13 +69,14 @@ class OsmIdSet { // bloom filter std::bitset* _bitset; + std::bitset* _bitsetNotIn; + mutable std::vector _blockEnds; mutable size_t _fsize; uint32_t knuth(uint32_t in) const; uint32_t jenkins(uint32_t in) const; - uint32_t hash(uint32_t in, int i) const; void diskAdd(osmid id); void close() const; void sort() const; diff --git a/src/pfaedle/osm/OsmReadOpts.h b/src/pfaedle/osm/OsmReadOpts.h index 47d78ae..5678c51 100644 --- a/src/pfaedle/osm/OsmReadOpts.h +++ b/src/pfaedle/osm/OsmReadOpts.h @@ -5,14 +5,14 @@ #ifndef PFAEDLE_OSM_OSMREADOPTS_H_ #define PFAEDLE_OSM_OSMREADOPTS_H_ +#include #include -#include +#include #include #include -#include +#include #include #include -#include #include "pfaedle/osm/Osm.h" #include "pfaedle/trgraph/Graph.h" #include "pfaedle/trgraph/Normalizer.h" @@ -77,11 +77,12 @@ struct RelLineRules { AttrLst sNameRule; AttrLst fromNameRule; AttrLst toNameRule; + AttrLst colorRule; }; inline bool operator==(const RelLineRules& a, const RelLineRules& b) { return a.sNameRule == b.sNameRule && a.fromNameRule == b.fromNameRule && - a.toNameRule == b.toNameRule; + a.toNameRule == b.toNameRule && a.colorRule == b.colorRule; } struct StationAttrRules { @@ -94,21 +95,6 @@ inline bool operator==(const StationAttrRules& a, const StationAttrRules& b) { return a.nameRule == b.nameRule && a.platformRule == b.platformRule; } -struct StatGroupNAttrRule { - DeepAttrRule attr; - double maxDist; -}; - -inline bool operator==(const StatGroupNAttrRule& a, - const StatGroupNAttrRule& b) { - return a.attr == b.attr && a.maxDist == b.maxDist; -} - -typedef std::unordered_map< - std::string, - std::unordered_map>> - StAttrGroups; - struct OsmReadOpts { OsmReadOpts() {} @@ -121,7 +107,7 @@ struct OsmReadOpts { MultAttrMap twoWayFilter; MultAttrMap stationFilter; MultAttrMap stationBlockerFilter; - std::vector statGroupNAttrRules; + MultAttrMap turnCycleFilter; trgraph::Normalizer statNormzer; trgraph::Normalizer lineNormzer; @@ -136,14 +122,23 @@ struct OsmReadOpts { uint8_t maxSnapLevel; double maxAngleSnapReach; - std::vector maxSnapDistances; - double maxSnapFallbackHeurDistance; + double maxSnapDistance; + double maxStationCandDistance; double maxBlockDistance; - double maxOsmStationDistance; + double maxSpeed; + double maxSpeedCorFac; - // TODO(patrick): this is not implemented yet - double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0}; + std::vector maxOsmStationDistances; + + // given in km/h, but store in m/s + double levelDefSpeed[8] = {85 * 0.2777, 70 * 0.2777, 55 * 0.2777, 40 * 0.2777, + 30 * 0.2777, 20 * 0.2777, 10 * 0.2777, 5 * 0.2777}; + + double oneWaySpeedPen; + double oneWayEntryCost; + + double noLinesPunishFact; double fullTurnAngle; @@ -154,9 +149,10 @@ struct OsmReadOpts { }; inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) { - if (a.maxSnapDistances.size() != b.maxSnapDistances.size()) return false; - for (size_t i = 0; i < a.maxSnapDistances.size(); i++) { - if (fabs(a.maxSnapDistances[i] - b.maxSnapDistances[i]) >= 0.1) + if (a.maxOsmStationDistances.size() != b.maxOsmStationDistances.size()) + return false; + for (size_t i = 0; i < a.maxOsmStationDistances.size(); i++) { + if (fabs(a.maxOsmStationDistances[i] - b.maxOsmStationDistances[i]) >= 0.1) return false; } @@ -173,24 +169,29 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) { a.twoWayFilter == b.twoWayFilter && a.stationFilter == b.stationFilter && a.stationBlockerFilter == b.stationBlockerFilter && - a.statGroupNAttrRules == b.statGroupNAttrRules && + a.turnCycleFilter == b.turnCycleFilter && a.statNormzer == b.statNormzer && a.lineNormzer == b.lineNormzer && a.trackNormzer == b.trackNormzer && a.relLinerules == b.relLinerules && a.statAttrRules == b.statAttrRules && a.maxSnapLevel == b.maxSnapLevel && fabs(a.maxAngleSnapReach - b.maxAngleSnapReach) < 0.1 && - fabs(a.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 && - fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) < - 0.1 && + fabs(a.maxSnapDistance - b.maxSnapDistance) < 0.1 && + fabs(a.maxStationCandDistance - b.maxStationCandDistance) < 0.1 && fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 && - fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 && - fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 && - fabs(a.levelSnapPunishFac[2] - b.levelSnapPunishFac[2]) < 0.1 && - fabs(a.levelSnapPunishFac[3] - b.levelSnapPunishFac[3]) < 0.1 && - fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 && - fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 && - fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 && + fabs(a.levelDefSpeed[0] - b.levelDefSpeed[0]) < 0.1 && + fabs(a.levelDefSpeed[1] - b.levelDefSpeed[1]) < 0.1 && + fabs(a.levelDefSpeed[2] - b.levelDefSpeed[2]) < 0.1 && + fabs(a.levelDefSpeed[3] - b.levelDefSpeed[3]) < 0.1 && + fabs(a.levelDefSpeed[4] - b.levelDefSpeed[4]) < 0.1 && + fabs(a.levelDefSpeed[5] - b.levelDefSpeed[5]) < 0.1 && + fabs(a.levelDefSpeed[6] - b.levelDefSpeed[6]) < 0.1 && + fabs(a.levelDefSpeed[7] - b.levelDefSpeed[7]) < 0.1 && + fabs(a.oneWaySpeedPen - b.oneWaySpeedPen) < 0.1 && + fabs(a.oneWayEntryCost - b.oneWayEntryCost) < 0.1 && + fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.1 && fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.1 && + fabs(a.maxSpeedCorFac - b.maxSpeedCorFac) < 0.1 && + fabs(a.maxSpeed - b.maxSpeed) < 0.1 && a.restrPosRestr == b.restrPosRestr && a.restrNegRestr == b.restrNegRestr && a.noRestrFilter == b.noRestrFilter; diff --git a/src/pfaedle/router/Comp.h b/src/pfaedle/router/Comp.h index 349ff04..cdadc21 100644 --- a/src/pfaedle/router/Comp.h +++ b/src/pfaedle/router/Comp.h @@ -16,7 +16,7 @@ namespace router { using util::editDist; // _____________________________________________________________________________ -inline double statSimi(const std::string& a, const std::string& b) { +inline bool statSimi(const std::string& a, const std::string& b) { if (a == b) return 1; if (a.empty() || b.empty()) return 0; @@ -55,7 +55,7 @@ inline double statSimi(const std::string& a, const std::string& b) { } // _____________________________________________________________________________ -inline double lineSimi(const std::string& a, const std::string& b) { +inline bool lineSimi(const std::string& a, const std::string& b) { if (a == b) return 1; if (a.empty() || b.empty()) return 0; diff --git a/src/pfaedle/router/EdgePL.cpp b/src/pfaedle/router/EdgePL.cpp deleted file mode 100644 index a166b5b..0000000 --- a/src/pfaedle/router/EdgePL.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#include "pfaedle/Def.h" -#include "util/geo/Geo.h" -#include "pfaedle/router/EdgePL.h" -#include "pfaedle/router/Router.h" -#include "util/String.h" - -using pfaedle::router::EdgePL; -using pfaedle::router::EdgeCost; -using pfaedle::router::EdgeList; -using pfaedle::trgraph::Node; - -// _____________________________________________________________________________ -EdgeList* EdgePL::getEdges() { return &_edges; } - -// _____________________________________________________________________________ -const EdgeList& EdgePL::getEdges() const { return _edges; } - -// _____________________________________________________________________________ -const POINT& EdgePL::frontHop() const { - if (!_edges.size()) return *_end->pl().getGeom(); - return _edges.back()->pl().frontHop(); -} - -// _____________________________________________________________________________ -const POINT& EdgePL::backHop() const { - if (!_edges.size()) return *_start->pl().getGeom(); - return _edges.front()->pl().backHop(); -} - -// _____________________________________________________________________________ -const Node* EdgePL::backNode() const { return _end; } - -// _____________________________________________________________________________ -const Node* EdgePL::frontNode() const { return _start; } - -// _____________________________________________________________________________ -const LINE* EdgePL::getGeom() const { - if (!_edges.size()) return 0; - if (!_geom.size()) { - const trgraph::Node* l = _start; - for (auto i = _edges.rbegin(); i != _edges.rend(); i++) { - const auto e = *i; - if ((e->getFrom() == l) ^ e->pl().isRev()) { - _geom.insert(_geom.end(), e->pl().getGeom()->begin(), - e->pl().getGeom()->end()); - } else { - _geom.insert(_geom.end(), e->pl().getGeom()->rbegin(), - e->pl().getGeom()->rend()); - } - l = e->getOtherNd(l); - } - } - - return &_geom; -} - -// _____________________________________________________________________________ -void EdgePL::setStartNode(const trgraph::Node* s) { _start = s; } - -// _____________________________________________________________________________ -void EdgePL::setEndNode(const trgraph::Node* e) { _end = e; } - -// _____________________________________________________________________________ -void EdgePL::setStartEdge(const trgraph::Edge* s) { _startE = s; } - -// _____________________________________________________________________________ -void EdgePL::setEndEdge(const trgraph::Edge* e) { _endE = e; } - -// _____________________________________________________________________________ -const EdgeCost& EdgePL::getCost() const { return _cost; } - -// _____________________________________________________________________________ -void EdgePL::setCost(const router::EdgeCost& c) { _cost = c; } - -// _____________________________________________________________________________ -util::json::Dict EdgePL::getAttrs() const { - util::json::Dict obj; - obj["cost"] = std::to_string(_cost.getValue()); - obj["from_edge"] = util::toString(_startE); - obj["to_edge"] = util::toString(_endE); - obj["dummy"] = _edges.size() ? "no" : "yes"; - - return obj; -} diff --git a/src/pfaedle/router/EdgePL.h b/src/pfaedle/router/EdgePL.h deleted file mode 100644 index 024b9a2..0000000 --- a/src/pfaedle/router/EdgePL.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_ROUTER_EDGEPL_H_ -#define PFAEDLE_ROUTER_EDGEPL_H_ - -#include -#include -#include "pfaedle/Def.h" -#include "pfaedle/router/Misc.h" -#include "util/geo/Geo.h" -#include "util/geo/GeoGraph.h" - -using util::geograph::GeoEdgePL; - -namespace pfaedle { -namespace router { - -class EdgePL { - public: - EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {} - const LINE* getGeom() const; - util::json::Dict getAttrs() const; - router::EdgeList* getEdges(); - const router::EdgeList& getEdges() const; - void setStartNode(const trgraph::Node* s); - void setEndNode(const trgraph::Node* s); - void setStartEdge(const trgraph::Edge* s); - void setEndEdge(const trgraph::Edge* s); - const router::EdgeCost& getCost() const; - void setCost(const router::EdgeCost& c); - const POINT& frontHop() const; - const POINT& backHop() const; - const trgraph::Node* frontNode() const; - const trgraph::Node* backNode() const; - - private: - router::EdgeCost _cost; - // the edges are in this field in REVERSED ORDER! - router::EdgeList _edges; - const trgraph::Node* _start; - const trgraph::Node* _end; - const trgraph::Edge* _startE; - const trgraph::Edge* _endE; - mutable LINE _geom; -}; -} // namespace router -} // namespace pfaedle - -#endif // PFAEDLE_ROUTER_EDGEPL_H_ diff --git a/src/pfaedle/router/Graph.h b/src/pfaedle/router/Graph.h deleted file mode 100644 index 88d7345..0000000 --- a/src/pfaedle/router/Graph.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_ROUTER_GRAPH_H_ -#define PFAEDLE_ROUTER_GRAPH_H_ - -#include "pfaedle/trgraph/Graph.h" -#include "pfaedle/router/EdgePL.h" -#include "pfaedle/router/NodePL.h" -#include "util/graph/DirGraph.h" - -using util::geo::Point; -using util::geo::Line; - -namespace pfaedle { -namespace router { - -typedef util::graph::Edge Edge; -typedef util::graph::Node Node; -typedef util::graph::DirGraph Graph; - -} // namespace router -} // namespace pfaedle - -#endif // PFAEDLE_ROUTER_GRAPH_H_ diff --git a/src/pfaedle/router/HopCache.cpp b/src/pfaedle/router/HopCache.cpp new file mode 100644 index 0000000..bb53290 --- /dev/null +++ b/src/pfaedle/router/HopCache.cpp @@ -0,0 +1,40 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include "pfaedle/router/HopCache.h" +#include "pfaedle/trgraph/Graph.h" +#include "util/Misc.h" + +using pfaedle::router::HopCache; +using pfaedle::trgraph::Edge; + +// _____________________________________________________________________________ +void HopCache::setMin(const Edge* a, const Edge* b, uint32_t val) { + _cache.set(a, b, val); +} + +// _____________________________________________________________________________ +void HopCache::setEx(const Edge* a, const Edge* b, uint32_t val) { + int64_t v = val; + _cache.set(a, b, -(v + 1)); +} + +// _____________________________________________________________________________ +void HopCache::setMin(const Edge* a, const std::set& b, uint32_t val) { + for (auto eb : b) _cache.set(a, eb, val); +} + +// _____________________________________________________________________________ +void HopCache::setMin(const std::set& a, const Edge* b, uint32_t val) { + for (auto ea : a) _cache.set(ea, b, val); +} + +// _____________________________________________________________________________ +std::pair HopCache::get(const Edge* a, const Edge* b) const { + int64_t v = _cache.get(a, b); + if (v < 0) return {(-v) - 1, 1}; + return {v, 0}; +} diff --git a/src/pfaedle/router/HopCache.h b/src/pfaedle/router/HopCache.h new file mode 100644 index 0000000..43d17af --- /dev/null +++ b/src/pfaedle/router/HopCache.h @@ -0,0 +1,39 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_HOPCACHE_H_ +#define PFAEDLE_ROUTER_HOPCACHE_H_ + +#include +#include +#include +#include "pfaedle/trgraph/Graph.h" +#include "util/Misc.h" + +namespace pfaedle { +namespace router { + +class HopCache { + public: + void setMin(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val); + + void setMin(const trgraph::Edge* a, const std::set& b, + uint32_t val); + + void setMin(const std::set& a, const trgraph::Edge* b, + uint32_t val); + + void setEx(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val); + + std::pair get(const trgraph::Edge* a, + const trgraph::Edge* b) const; + + private: + util::SparseMatrix _cache; +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_HOPCACHE_H_ diff --git a/src/pfaedle/router/Misc.h b/src/pfaedle/router/Misc.h index 1c69c40..237c8b1 100644 --- a/src/pfaedle/router/Misc.h +++ b/src/pfaedle/router/Misc.h @@ -21,132 +21,75 @@ using ad::cppgtfs::gtfs::Stop; namespace pfaedle { namespace router { -struct NodeCand { - trgraph::Node* nd; - double pen; -}; +extern double time; struct EdgeCand { trgraph::Edge* e; double pen; + double progr; + POINT point; + int time; + std::vector depPrede; }; struct RoutingOpts { RoutingOpts() - : fullTurnPunishFac(2000), + : fullTurnPunishFac(1000), fullTurnAngle(45), - passThruStationsPunish(100), - oneWayPunishFac(1), - oneWayEdgePunish(0), - lineUnmatchedPunishFact(0.5), - noLinesPunishFact(0), + lineUnmatchedPunishFact(1), + lineNameFromUnmatchedPunishFact(1), + lineNameToUnmatchedPunishFact(1), + noLinesPunishFact(1), platformUnmatchedPen(0), stationDistPenFactor(0), + turnRestrCost(0), popReachEdge(true), noSelfHops(true) {} - double fullTurnPunishFac; + uint32_t fullTurnPunishFac; double fullTurnAngle; - double passThruStationsPunish; - double oneWayPunishFac; - double oneWayEdgePunish; double lineUnmatchedPunishFact; + double lineNameFromUnmatchedPunishFact; + double lineNameToUnmatchedPunishFact; double noLinesPunishFact; double platformUnmatchedPen; + double stationUnmatchedPen; double stationDistPenFactor; - double nonOsmPen; - double levelPunish[8]; + double nonStationPen; + uint32_t turnRestrCost; bool popReachEdge; bool noSelfHops; + bool useStations; + double transitionPen; + std::string transPenMethod; }; // _____________________________________________________________________________ inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) { - return fabs(a.fullTurnPunishFac - b.fullTurnPunishFac) < 0.01 && + return a.fullTurnPunishFac == b.fullTurnPunishFac && fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.01 && - fabs(a.passThruStationsPunish - b.passThruStationsPunish) < 0.01 && - fabs(a.oneWayPunishFac - b.oneWayPunishFac) < 0.01 && - fabs(a.oneWayEdgePunish - b.oneWayEdgePunish) < 0.01 && fabs(a.lineUnmatchedPunishFact - b.lineUnmatchedPunishFact) < 0.01 && + fabs(a.lineNameFromUnmatchedPunishFact - + b.lineNameFromUnmatchedPunishFact) < 0.01 && + fabs(a.lineNameToUnmatchedPunishFact - + b.lineNameToUnmatchedPunishFact) < 0.01 && fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.01 && fabs(a.platformUnmatchedPen - b.platformUnmatchedPen) < 0.01 && + fabs(a.stationUnmatchedPen - b.stationUnmatchedPen) < 0.01 && fabs(a.stationDistPenFactor - b.stationDistPenFactor) < 0.01 && - fabs(a.nonOsmPen - b.nonOsmPen) < 0.01 && - fabs(a.levelPunish[0] - b.levelPunish[0]) < 0.01 && - fabs(a.levelPunish[1] - b.levelPunish[1]) < 0.01 && - fabs(a.levelPunish[2] - b.levelPunish[2]) < 0.01 && - fabs(a.levelPunish[3] - b.levelPunish[3]) < 0.01 && - fabs(a.levelPunish[4] - b.levelPunish[4]) < 0.01 && - fabs(a.levelPunish[5] - b.levelPunish[5]) < 0.01 && - fabs(a.levelPunish[6] - b.levelPunish[6]) < 0.01 && - fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01 && - a.popReachEdge == b.popReachEdge && a.noSelfHops == b.noSelfHops; -} - -struct EdgeCost { - EdgeCost() : _cost(0) {} - explicit EdgeCost(double cost) : _cost(cost) {} - EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3, - double mDistLvl4, double mDistLvl5, double mDistLvl6, - double mDistLvl7, uint32_t fullTurns, int32_t passThru, - double oneWayMeters, size_t oneWayEdges, double lineUnmatchedMeters, - double noLinesMeters, double reachPen, const RoutingOpts* o) { - if (!o) { - _cost = mDist + reachPen; - } else { - _cost = mDist * o->levelPunish[0] + mDistLvl1 * o->levelPunish[1] + - mDistLvl2 * o->levelPunish[2] + mDistLvl3 * o->levelPunish[3] + - mDistLvl4 * o->levelPunish[4] + mDistLvl5 * o->levelPunish[5] + - mDistLvl6 * o->levelPunish[6] + mDistLvl7 * o->levelPunish[7] + - oneWayMeters * o->oneWayPunishFac + - oneWayEdges * o->oneWayEdgePunish + - lineUnmatchedMeters * o->lineUnmatchedPunishFact + - noLinesMeters * o->noLinesPunishFact + - fullTurns * o->fullTurnPunishFac + - passThru * o->passThruStationsPunish + reachPen; - } - } - - float _cost; - - double getValue() const { return _cost; } -}; - -// _____________________________________________________________________________ -inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) { - return EdgeCost(a.getValue() + b.getValue()); -} - -// _____________________________________________________________________________ -inline bool operator<=(const EdgeCost& a, const EdgeCost& b) { - return a.getValue() <= b.getValue(); -} - -// _____________________________________________________________________________ -inline bool operator==(const EdgeCost& a, const EdgeCost& b) { - return a.getValue() == b.getValue(); -} - -// _____________________________________________________________________________ -inline bool operator>(const EdgeCost& a, const EdgeCost& b) { - return a.getValue() > b.getValue(); -} - -// _____________________________________________________________________________ -template -inline bool angSmaller(const Point& f, const Point& m, const Point& t, - double ang) { - if (util::geo::innerProd(m, f, t) < ang) return 1; - return 0; + a.turnRestrCost == b.turnRestrCost && + fabs(a.transitionPen - b.transitionPen) < 0.01 && + fabs(a.nonStationPen - b.nonStationPen) < 0.01 && + a.transPenMethod == b.transPenMethod && + a.useStations == b.useStations && a.popReachEdge == b.popReachEdge && + a.noSelfHops == b.noSelfHops; } typedef std::set NodeSet; typedef std::set EdgeSet; typedef std::unordered_map FeedStops; -typedef std::vector NodeCandGroup; -typedef std::vector NodeCandRoute; - typedef std::vector EdgeCandGroup; +typedef std::vector EdgeCandMap; typedef std::vector EdgeCandRoute; typedef std::vector EdgeList; @@ -154,8 +97,12 @@ typedef std::vector NodeList; struct EdgeListHop { EdgeList edges; - const trgraph::Node* start; - const trgraph::Node* end; + const trgraph::Edge* start; + const trgraph::Edge* end; + double progrStart; + double progrEnd; + POINT pointStart; + POINT pointEnd; }; typedef std::vector EdgeListHops; diff --git a/src/pfaedle/router/NodePL.h b/src/pfaedle/router/NodePL.h deleted file mode 100644 index a9c2ea4..0000000 --- a/src/pfaedle/router/NodePL.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_ROUTER_NODEPL_H_ -#define PFAEDLE_ROUTER_NODEPL_H_ - -#include -#include -#include "pfaedle/trgraph/Graph.h" -#include "util/geo/GeoGraph.h" -#include "util/geo/Geo.h" -#include "pfaedle/Def.h" - -using util::geograph::GeoNodePL; - - -namespace pfaedle { -namespace router { - -class NodePL { - public: - NodePL() : _n(0) {} - NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT - - const POINT* getGeom() const { - return !_n ? 0 : _n->pl().getGeom(); - } - util::json::Dict getAttrs() const { - if (_n) return _n->pl().getAttrs(); - return util::json::Dict(); - } - - private: - const pfaedle::trgraph::Node* _n; -}; -} // namespace router -} // namespace pfaedle - -#endif // PFAEDLE_ROUTER_NODEPL_H_ diff --git a/src/pfaedle/router/Router.cpp b/src/pfaedle/router/Router.cpp deleted file mode 100644 index c2e0055..0000000 --- a/src/pfaedle/router/Router.cpp +++ /dev/null @@ -1,646 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifdef _OPENMP -#include -#else -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include "pfaedle/router/Comp.h" -#include "pfaedle/router/Router.h" -#include "pfaedle/router/RoutingAttrs.h" -#include "util/geo/output/GeoGraphJsonOutput.h" -#include "util/graph/Dijkstra.h" -#include "util/graph/EDijkstra.h" -#include "util/log/Log.h" - -using pfaedle::router::Router; -using pfaedle::router::EdgeCost; -using pfaedle::router::CostFunc; -using pfaedle::router::DistHeur; -using pfaedle::router::NCostFunc; -using pfaedle::router::NDistHeur; -using pfaedle::router::CombCostFunc; -using pfaedle::router::EdgeListHop; -using pfaedle::router::EdgeListHops; -using pfaedle::router::RoutingOpts; -using pfaedle::router::RoutingAttrs; -using pfaedle::router::HopBand; -using pfaedle::router::NodeCandRoute; -using util::graph::EDijkstra; -using util::graph::Dijkstra; -using util::geo::webMercMeterDist; - -// _____________________________________________________________________________ -EdgeCost NCostFunc::operator()(const trgraph::Node* from, - const trgraph::Edge* e, - const trgraph::Node* to) const { - UNUSED(to); - if (!from) return EdgeCost(); - - int oneway = e->pl().oneWay() == 2; - int32_t stationSkip = 0; - - return EdgeCost(e->pl().lvl() == 0 ? e->pl().getLength() : 0, - e->pl().lvl() == 1 ? e->pl().getLength() : 0, - e->pl().lvl() == 2 ? e->pl().getLength() : 0, - e->pl().lvl() == 3 ? e->pl().getLength() : 0, - e->pl().lvl() == 4 ? e->pl().getLength() : 0, - e->pl().lvl() == 5 ? e->pl().getLength() : 0, - e->pl().lvl() == 6 ? e->pl().getLength() : 0, - e->pl().lvl() == 7 ? e->pl().getLength() : 0, 0, stationSkip, - e->pl().getLength() * oneway, oneway, 0, 0, 0, &_rOpts); -} - -// _____________________________________________________________________________ -EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n, - const trgraph::Edge* to) const { - if (!from) return EdgeCost(); - - uint32_t fullTurns = 0; - int oneway = from->pl().oneWay() == 2; - int32_t stationSkip = 0; - - if (n) { - if (from->getFrom() == to->getTo() && from->getTo() == to->getFrom()) { - // trivial full turn - fullTurns = 1; - } else if (n->getDeg() > 2) { - // otherwise, only intersection angles will be punished - fullTurns = router::angSmaller(from->pl().backHop(), *n->pl().getGeom(), - to->pl().frontHop(), _rOpts.fullTurnAngle); - } - - if (from->pl().isRestricted() && !_res.may(from, to, n)) oneway = 1; - - // for debugging - n->pl().setVisited(); - - if (_tgGrp && n->pl().getSI() && n->pl().getSI()->getGroup() != _tgGrp) - stationSkip = 1; - } - - double transitLinePen = transitLineCmp(from->pl()); - bool noLines = (_rAttrs.shortName.empty() && _rAttrs.toString.empty() && - _rAttrs.fromString.empty() && from->pl().getLines().empty()); - - return EdgeCost(from->pl().lvl() == 0 ? from->pl().getLength() : 0, - from->pl().lvl() == 1 ? from->pl().getLength() : 0, - from->pl().lvl() == 2 ? from->pl().getLength() : 0, - from->pl().lvl() == 3 ? from->pl().getLength() : 0, - from->pl().lvl() == 4 ? from->pl().getLength() : 0, - from->pl().lvl() == 5 ? from->pl().getLength() : 0, - from->pl().lvl() == 6 ? from->pl().getLength() : 0, - from->pl().lvl() == 7 ? from->pl().getLength() : 0, fullTurns, - stationSkip, from->pl().getLength() * oneway, oneway, - from->pl().getLength() * transitLinePen, - noLines ? from->pl().getLength() : 0, 0, &_rOpts); -} - -// _____________________________________________________________________________ -double CostFunc::transitLineCmp(const trgraph::EdgePL& e) const { - if (_rAttrs.shortName.empty() && _rAttrs.toString.empty() && - _rAttrs.fromString.empty()) - return 0; - double best = 1; - for (const auto* l : e.getLines()) { - double cur = _rAttrs.simi(l); - - if (cur < 0.0001) return 0; - if (cur < best) best = cur; - } - - return best; -} - -// _____________________________________________________________________________ -NDistHeur::NDistHeur(const RoutingOpts& rOpts, - const std::set& tos) - : _rOpts(rOpts), _maxCentD(0) { - size_t c = 0; - double x = 0, y = 0; - for (auto to : tos) { - x += to->pl().getGeom()->getX(); - y += to->pl().getGeom()->getY(); - c++; - } - - x /= c; - y /= c; - _center = POINT(x, y); - - for (auto to : tos) { - double cur = webMercMeterDist(*to->pl().getGeom(), _center); - if (cur > _maxCentD) _maxCentD = cur; - } -} - -// _____________________________________________________________________________ -DistHeur::DistHeur(uint8_t minLvl, const RoutingOpts& rOpts, - const std::set& tos) - : _rOpts(rOpts), _lvl(minLvl), _maxCentD(0) { - size_t c = 0; - double x = 0, y = 0; - for (auto to : tos) { - x += to->getFrom()->pl().getGeom()->getX(); - y += to->getFrom()->pl().getGeom()->getY(); - c++; - } - - x /= c; - y /= c; - _center = POINT(x, y); - - for (auto to : tos) { - double cur = webMercMeterDist(*to->getFrom()->pl().getGeom(), _center) * - _rOpts.levelPunish[_lvl]; - if (cur > _maxCentD) _maxCentD = cur; - } -} - -// _____________________________________________________________________________ -EdgeCost DistHeur::operator()(const trgraph::Edge* a, - const std::set& b) const { - UNUSED(b); - double cur = webMercMeterDist(*a->getFrom()->pl().getGeom(), _center) * - _rOpts.levelPunish[_lvl]; - - return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -} - -// _____________________________________________________________________________ -EdgeCost NDistHeur::operator()(const trgraph::Node* a, - const std::set& b) const { - UNUSED(b); - double cur = webMercMeterDist(*a->pl().getGeom(), _center); - - return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -} - -// _____________________________________________________________________________ -double CombCostFunc::operator()(const router::Edge* from, const router::Node* n, - const router::Edge* to) const { - UNUSED(n); - UNUSED(from); - return to->pl().getCost().getValue(); -} - -// _____________________________________________________________________________ -Router::Router(size_t numThreads, bool caching) - : _cache(numThreads), _caching(caching) { - for (size_t i = 0; i < numThreads; i++) { - _cache[i] = new Cache(); - } -} - -// _____________________________________________________________________________ -Router::~Router() { - for (size_t i = 0; i < _cache.size(); i++) { - delete _cache[i]; - } -} - -// _____________________________________________________________________________ -bool Router::compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const { - for (auto n1 : a) { - for (auto n2 : b) { - if (n1.e->getFrom()->pl().getComp() == n2.e->getFrom()->pl().getComp()) - return true; - } - } - - return false; -} - -// _____________________________________________________________________________ -HopBand Router::getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& b, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - assert(a.size()); - assert(b.size()); - - double pend = 0; - for (size_t i = 0; i < a.size(); i++) { - for (size_t j = 0; j < b.size(); j++) { - double d = webMercMeterDist(*a[i].e->getFrom()->pl().getGeom(), - *b[j].e->getFrom()->pl().getGeom()); - if (d > pend) pend = d; - } - } - - LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters"; - - const trgraph::StatGroup* tgGrpTo = 0; - - if (b.begin()->e->getFrom()->pl().getSI()) - tgGrpTo = b.begin()->e->getFrom()->pl().getSI()->getGroup(); - - CostFunc costF(rAttrs, rOpts, rest, tgGrpTo, pend * 50); - - std::set from, to; - - for (auto e : a) from.insert(e.e); - for (auto e : b) to.insert(e.e); - - LOG(VDEBUG) << "Doing pilot run between " << from.size() << "->" << to.size() - << " edge candidates"; - - EdgeList el; - EdgeCost ret = costF.inf(); - DistHeur distH(0, rOpts, to); - - if (compConned(a, b)) - ret = EDijkstra::shortestPath(from, to, costF, distH, &el); - - if (el.size() < 2 && costF.inf() <= ret) { - LOG(VDEBUG) << "Pilot run: no connection between candidate groups," - << " setting max distance to 1"; - return HopBand{0, 1, 0, 0}; - } - - // cache the found path, will save a few dijkstra iterations - nestedCache(&el, from, costF, rAttrs); - - auto na = el.back()->getFrom(); - auto nb = el.front()->getFrom(); - - double maxStrD = 0; - - for (auto e : to) { - double d = webMercMeterDist(*el.front()->getFrom()->pl().getGeom(), - *e->getTo()->pl().getGeom()); - if (d > maxStrD) maxStrD = d; - } - - // TODO(patrick): derive the punish level here automatically - double maxD = std::max(ret.getValue(), pend * rOpts.levelPunish[2]) * 3 + - rOpts.fullTurnPunishFac + rOpts.platformUnmatchedPen; - double minD = ret.getValue(); - - LOG(VDEBUG) << "Pilot run: min distance between two groups is " - << ret.getValue() << " (between nodes " << na << " and " << nb - << "), using a max routing distance of " << maxD << ". The max" - << " straight line distance from the pilot target to any other " - "target node was" - << " " << maxStrD << "."; - - return HopBand{minD, maxD, el.front(), maxStrD}; -} - -// _____________________________________________________________________________ -EdgeListHops Router::routeGreedy(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - if (route.size() < 2) return EdgeListHops(); - EdgeListHops ret(route.size() - 1); - - for (size_t i = 0; i < route.size() - 1; i++) { - const trgraph::StatGroup* tgGrp = 0; - std::set from, to; - for (auto c : route[i]) from.insert(c.nd); - for (auto c : route[i + 1]) to.insert(c.nd); - if (route[i + 1].begin()->nd->pl().getSI()) - tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup(); - - NCostFunc cost(rAttrs, rOpts, rest, tgGrp); - NDistHeur dist(rOpts, to); - - NodeList nodesRet; - EdgeListHop hop; - Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet); - - if (nodesRet.size() > 1) { - // careful: nodesRet is reversed! - hop.start = nodesRet.back(); - hop.end = nodesRet.front(); - } else { - // just take the first candidate if no route could be found - hop.start = *from.begin(); - hop.end = *to.begin(); - } - - ret[i] = hop; - } - - return ret; -} - -// _____________________________________________________________________________ -EdgeListHops Router::routeGreedy2(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - if (route.size() < 2) return EdgeListHops(); - EdgeListHops ret(route.size() - 1); - - for (size_t i = 0; i < route.size() - 1; i++) { - const trgraph::StatGroup* tgGrp = 0; - std::set from, to; - - if (i == 0) - for (auto c : route[i]) from.insert(c.nd); - else - from.insert(const_cast(ret[i - 1].end)); - - for (auto c : route[i + 1]) to.insert(c.nd); - - if (route[i + 1].begin()->nd->pl().getSI()) - tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup(); - - NCostFunc cost(rAttrs, rOpts, rest, tgGrp); - NDistHeur dist(rOpts, to); - - NodeList nodesRet; - EdgeListHop hop; - Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet); - if (nodesRet.size() > 1) { - // careful: nodesRet is reversed! - hop.start = nodesRet.back(); - hop.end = nodesRet.front(); - } else { - // just take the first candidate if no route could be found - hop.start = *from.begin(); - hop.end = *to.begin(); - } - - ret[i] = hop; - } - - return ret; -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const EdgeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - router::Graph cg; - return Router::route(route, rAttrs, rOpts, rest, &cg); -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const EdgeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, - router::Graph* cgraph) const { - if (route.size() < 2) return EdgeListHops(); - EdgeListHops ret(route.size() - 1); - - CombCostFunc ccost(rOpts); - router::Node* source = cgraph->addNd(); - router::Node* sink = cgraph->addNd(); - CombNodeMap nodes; - CombNodeMap nextNodes; - - for (size_t i = 0; i < route[0].size(); i++) { - auto e = route[0][i].e; - // we can be sure that each edge is exactly assigned to only one - // node because the transitgraph is directed - nodes[e] = cgraph->addNd(route[0][i].e->getFrom()); - cgraph->addEdg(source, nodes[e]) - ->pl() - .setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - route[0][i].pen, 0)); - } - - size_t iters = EDijkstra::ITERS; - double itPerSecTot = 0; - size_t n = 0; - for (size_t i = 0; i < route.size() - 1; i++) { - nextNodes.clear(); - HopBand hopBand = getHopBand(route[i], route[i + 1], rAttrs, rOpts, rest); - - const trgraph::StatGroup* tgGrp = 0; - if (route[i + 1].begin()->e->getFrom()->pl().getSI()) - tgGrp = route[i + 1].begin()->e->getFrom()->pl().getSI()->getGroup(); - - std::set froms; - for (const auto& fr : route[i]) froms.insert(fr.e); - - for (auto eFr : froms) { - router::Node* cNodeFr = nodes.find(eFr)->second; - - EdgeSet tos; - std::map edges; - std::map pens; - std::unordered_map edgeLists; - std::unordered_map costs; - - assert(route[i + 1].size()); - - for (const auto& to : route[i + 1]) { - auto eTo = to.e; - tos.insert(eTo); - if (!nextNodes.count(eTo)) - nextNodes[eTo] = cgraph->addNd(to.e->getFrom()); - if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink); - - edges[eTo] = cgraph->addEdg(cNodeFr, nextNodes[eTo]); - pens[eTo] = to.pen; - - edgeLists[eTo] = edges[eTo]->pl().getEdges(); - edges[eTo]->pl().setStartNode(eFr->getFrom()); - // for debugging - edges[eTo]->pl().setStartEdge(eFr); - edges[eTo]->pl().setEndNode(to.e->getFrom()); - // for debugging - edges[eTo]->pl().setEndEdge(eTo); - } - - size_t iters = EDijkstra::ITERS; - auto t1 = TIME(); - - assert(tos.size()); - assert(froms.size()); - - hops(eFr, froms, tos, tgGrp, edgeLists, &costs, rAttrs, rOpts, rest, - hopBand); - double itPerSec = - (static_cast(EDijkstra::ITERS - iters)) / TOOK(t1, TIME()); - n++; - itPerSecTot += itPerSec; - - LOG(VDEBUG) << "from " << eFr << ": 1-" << tos.size() << " (" - << route[i + 1].size() << " nodes) hop took " - << EDijkstra::ITERS - iters << " iterations, " - << TOOK(t1, TIME()) << "ms (tput: " << itPerSec << " its/ms)"; - for (auto& kv : edges) { - kv.second->pl().setCost( - EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) + - costs[kv.first]); - - if (rOpts.popReachEdge && kv.second->pl().getEdges()->size()) { - if (kv.second->pl().getEdges() && - kv.second->pl().getEdges()->size()) { - // the reach edge is included, but we dont want it in the geometry - kv.second->pl().getEdges()->erase( - kv.second->pl().getEdges()->begin()); - } - } - } - } - - std::swap(nodes, nextNodes); - } - - LOG(VDEBUG) << "Hops took " << EDijkstra::ITERS - iters << " iterations," - << " average tput was " << (itPerSecTot / n) << " its/ms"; - - iters = EDijkstra::ITERS; - std::vector res; - EDijkstra::shortestPath(source, sink, ccost, &res); - size_t j = 0; - - LOG(VDEBUG) << "Optim graph solve took " << EDijkstra::ITERS - iters - << " iterations."; - - for (auto i = res.rbegin(); i != res.rend(); i++) { - const auto e = *i; - if (e->getFrom() != source && e->getTo() != sink) { - assert(e->pl().frontNode()); - assert(e->pl().backNode()); - - ret[j] = EdgeListHop{std::move(*e->pl().getEdges()), e->pl().frontNode(), - e->pl().backNode()}; - j++; - } - } - - assert(ret.size() == j); - return ret; -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const { - router::Graph cg; - return Router::route(route, rAttrs, rOpts, rest, &cg); -} - -// _____________________________________________________________________________ -EdgeListHops Router::route(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, - router::Graph* cgraph) const { - EdgeCandRoute r; - for (auto& nCands : route) { - r.emplace_back(); - for (auto n : nCands) - for (auto* e : n.nd->getAdjListOut()) - r.back().push_back(EdgeCand{e, n.pen}); - } - - return Router::route(r, rAttrs, rOpts, rest, cgraph); -} - -// _____________________________________________________________________________ -void Router::hops(trgraph::Edge* from, const std::set& froms, - const std::set tos, - const trgraph::StatGroup* tgGrp, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, HopBand hopB) const { - std::set rem; - - CostFunc cost(rAttrs, rOpts, rest, tgGrp, hopB.maxD); - - const auto& cached = getCachedHops(from, tos, edgesRet, rCosts, rAttrs); - - for (auto e : cached) { - // shortcut: if the nodes lie in two different connected components, - // the distance between them is trivially infinite - if ((rOpts.noSelfHops && (e == from || e->getFrom() == from->getFrom())) || - from->getFrom()->pl().getComp() != e->getTo()->pl().getComp() || - e->pl().oneWay() == 2 || from->pl().oneWay() == 2) { - (*rCosts)[e] = cost.inf(); - } else { - rem.insert(e); - } - } - - LOG(VDEBUG) << "From cache: " << tos.size() - rem.size() - << ", have to cal: " << rem.size(); - - if (rem.size()) { - DistHeur dist(from->getFrom()->pl().getComp()->minEdgeLvl, rOpts, rem); - const auto& ret = EDijkstra::shortestPath(from, rem, cost, dist, edgesRet); - for (const auto& kv : ret) { - nestedCache(edgesRet.at(kv.first), froms, cost, rAttrs); - - (*rCosts)[kv.first] = kv.second; - } - } -} - -// _____________________________________________________________________________ -void Router::nestedCache(const EdgeList* el, - const std::set& froms, - const CostFunc& cost, - const RoutingAttrs& rAttrs) const { - if (!_caching) return; - if (el->size() == 0) return; - // iterate over result edges backwards - EdgeList curEdges; - EdgeCost curCost; - - size_t j = 0; - - for (auto i = el->begin(); i < el->end(); i++) { - if (curEdges.size()) { - curCost = curCost + cost(*i, (*i)->getTo(), curEdges.back()); - } - - curEdges.push_back(*i); - - if (froms.count(*i)) { - EdgeCost startC = cost(0, 0, *i) + curCost; - cache(*i, el->front(), startC, &curEdges, rAttrs); - j++; - } - } -} - -// _____________________________________________________________________________ -std::set Router::getCachedHops( - trgraph::Edge* from, const std::set& tos, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs) const { - std::set ret; - for (auto to : tos) { - if (_caching && (*_cache[omp_get_thread_num()])[rAttrs][from].count(to)) { - const auto& cv = (*_cache[omp_get_thread_num()])[rAttrs][from][to]; - (*rCosts)[to] = cv.first; - *edgesRet.at(to) = cv.second; - } else { - ret.insert(to); - } - } - - return ret; -} - -// _____________________________________________________________________________ -void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c, - EdgeList* edges, const RoutingAttrs& rAttrs) const { - if (!_caching) return; - if (from == to) return; - (*_cache[omp_get_thread_num()])[rAttrs][from][to] = - std::pair(c, *edges); -} - -// _____________________________________________________________________________ -size_t Router::getCacheNumber() const { return _cache.size(); } diff --git a/src/pfaedle/router/Router.h b/src/pfaedle/router/Router.h index 74bf581..4ec8cb7 100644 --- a/src/pfaedle/router/Router.h +++ b/src/pfaedle/router/Router.h @@ -6,198 +6,97 @@ #define PFAEDLE_ROUTER_ROUTER_H_ #include -#include -#include #include +#include #include #include +#include #include #include #include "pfaedle/Def.h" #include "pfaedle/osm/Restrictor.h" -#include "pfaedle/router/Graph.h" +#include "pfaedle/router/HopCache.h" #include "pfaedle/router/Misc.h" #include "pfaedle/router/RoutingAttrs.h" +#include "pfaedle/router/TripTrie.h" +#include "pfaedle/router/Weights.h" #include "pfaedle/trgraph/Graph.h" +#include "util/Misc.h" #include "util/geo/Geo.h" -#include "util/graph/Dijkstra.h" #include "util/graph/EDijkstra.h" -using util::graph::EDijkstra; -using util::graph::Dijkstra; - namespace pfaedle { namespace router { -typedef std::unordered_map CombNodeMap; +constexpr static uint32_t ROUTE_INF = std::numeric_limits::max(); +constexpr static double DBL_INF = std::numeric_limits::infinity(); +constexpr static size_t NO_PREDE = std::numeric_limits::max(); + +constexpr static int MAX_ROUTE_COST_DOUBLING_STEPS = 3; + typedef std::pair HId; -typedef std::map< - RoutingAttrs, - std::unordered_map > > > - Cache; +typedef std::vector LayerCostsDAG; +typedef std::vector CostsDAG; +typedef std::vector> PredeDAG; -struct HopBand { - double minD; - double maxD; - const trgraph::Edge* nearest; - double maxInGrpDist; -}; +typedef std::unordered_map> + EdgeCostMatrix; +typedef std::unordered_map> + EdgeDistMatrix; +typedef util::graph::EDijkstra::EList TrEList; -struct CostFunc - : public EDijkstra::CostFunc { - CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& res, const trgraph::StatGroup* tgGrp, - double max) - : _rAttrs(rAttrs), - _rOpts(rOpts), - _res(res), - _tgGrp(tgGrp), - _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, max, 0) {} +typedef std::vector, uint32_t>> CostMatrix; - const RoutingAttrs& _rAttrs; - const RoutingOpts& _rOpts; - const osm::Restrictor& _res; - const trgraph::StatGroup* _tgGrp; - EdgeCost _inf; - - EdgeCost operator()(const trgraph::Edge* from, const trgraph::Node* n, - const trgraph::Edge* to) const; - EdgeCost inf() const { return _inf; } - - double transitLineCmp(const trgraph::EdgePL& e) const; -}; - -struct NCostFunc - : public Dijkstra::CostFunc { - NCostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& res, const trgraph::StatGroup* tgGrp) - : _rAttrs(rAttrs), - _rOpts(rOpts), - _res(res), - _tgGrp(tgGrp), - _inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - std::numeric_limits::infinity(), 0) {} - - const RoutingAttrs& _rAttrs; - const RoutingOpts& _rOpts; - const osm::Restrictor& _res; - const trgraph::StatGroup* _tgGrp; - EdgeCost _inf; - - EdgeCost operator()(const trgraph::Node* from, const trgraph::Edge* e, - const trgraph::Node* to) const; - EdgeCost inf() const { return _inf; } - - double transitLineCmp(const trgraph::EdgePL& e) const; -}; - -struct DistHeur - : public EDijkstra::HeurFunc { - DistHeur(uint8_t minLvl, const RoutingOpts& rOpts, - const std::set& tos); - - const RoutingOpts& _rOpts; - uint8_t _lvl; - POINT _center; - double _maxCentD; - EdgeCost operator()(const trgraph::Edge* a, - const std::set& b) const; -}; - -struct NDistHeur - : public Dijkstra::HeurFunc { - NDistHeur(const RoutingOpts& rOpts, const std::set& tos); - - const RoutingOpts& _rOpts; - POINT _center; - double _maxCentD; - EdgeCost operator()(const trgraph::Node* a, - const std::set& b) const; -}; - -struct CombCostFunc - : public EDijkstra::CostFunc { - explicit CombCostFunc(const RoutingOpts& rOpts) : _rOpts(rOpts) {} - - const RoutingOpts& _rOpts; - - double operator()(const router::Edge* from, const router::Node* n, - const router::Edge* to) const; - double inf() const { return std::numeric_limits::infinity(); } +class Router { + public: + virtual ~Router() = default; + virtual std::map route(const TripTrie* trie, + const EdgeCandMap& ecm, + const RoutingOpts& rOpts, + const osm::Restrictor& rest, + HopCache* hopCache, + bool noFastHops) const = 0; }; /* * Finds the most likely route of schedule-based vehicle between stops in a * physical transportation network */ -class Router { +template +class RouterImpl : public Router { public: - // Init this router with caches for numThreads threads - explicit Router(size_t numThreads, bool caching); - ~Router(); - - // Find the most likely path through the graph for a node candidate route. - EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, const osm::Restrictor& rest, - router::Graph* cgraph) const; - - // Find the most likely path through the graph for an edge candidate route. - EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, const osm::Restrictor& rest, - router::Graph* cgraph) const; - - // Find the most likely path through cgraph for a node candidate route, but - // based on a greedy node to node approach - EdgeListHops routeGreedy(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - - // Find the most likely path through cgraph for a node candidate route, but - // based on a greedy node to node set approach - EdgeListHops routeGreedy2(const NodeCandRoute& route, - const RoutingAttrs& rAttrs, - const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; - - // Return the number of thread caches this router was initialized with - size_t getCacheNumber() const; + // Find the most likely path through the graph for a trip trie. + virtual std::map route( + const TripTrie* trie, const EdgeCandMap& ecm, const RoutingOpts& rOpts, + const osm::Restrictor& rest, HopCache* hopCache, bool noFastHops) const; private: - mutable std::vector _cache; - bool _caching; - HopBand getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& b, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest) const; + void hops(const EdgeCandGroup& from, const EdgeCandGroup& to, + CostMatrix* rCosts, CostMatrix* dists, const RoutingAttrs& rAttrs, + const RoutingOpts& rOpts, const osm::Restrictor& rest, + HopCache* hopCache, uint32_t maxCost) const; - void hops(trgraph::Edge* from, const std::set& froms, - const std::set to, const trgraph::StatGroup* tgGrp, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, - const osm::Restrictor& rest, HopBand hopB) const; + void hopsFast(const EdgeCandGroup& from, const EdgeCandGroup& to, + const LayerCostsDAG& initCosts, CostMatrix* rCosts, + const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& rest, - std::set getCachedHops( - trgraph::Edge* from, const std::set& to, - const std::unordered_map& edgesRet, - std::unordered_map* rCosts, - const RoutingAttrs& rAttrs) const; + HopCache* hopCache, uint32_t maxCost) const; - void cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c, - EdgeList* edges, const RoutingAttrs& rAttrs) const; + bool connected(const EdgeCand& from, const EdgeCandGroup& tos) const; + bool connected(const EdgeCandGroup& froms, const EdgeCand& to) const; - void nestedCache(const EdgeList* el, const std::set& froms, - const CostFunc& cost, const RoutingAttrs& rAttrs) const; + bool cacheDrop( - bool compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const; + HopCache* hopCache, const std::set& froms, + const trgraph::Edge* to, uint32_t maxCost) const; + + uint32_t addNonOverflow(uint32_t a, uint32_t b) const; }; + +#include "pfaedle/router/Router.tpp" } // namespace router } // namespace pfaedle diff --git a/src/pfaedle/router/Router.tpp b/src/pfaedle/router/Router.tpp new file mode 100644 index 0000000..9b35f51 --- /dev/null +++ b/src/pfaedle/router/Router.tpp @@ -0,0 +1,614 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifdef _OPENMP +#include +#else +#define omp_get_thread_num() 0 +#define omp_get_num_procs() 1 +#endif + +#include +#include +#include +#include +#include +#include +#include + +using util::graph::EDijkstra; + +// _____________________________________________________________________________ +template +std::map RouterImpl::route( + const TripTrie* trie, const EdgeCandMap& ecm, const RoutingOpts& rOpts, + const osm::Restrictor& rest, HopCache* hopCache, bool noFastHops) const { + std::map ret; + + // the current node costs in our DAG + CostsDAG costsDAG(trie->getNds().size()); + PredeDAG predeDAG(trie->getNds().size()); + std::vector maxCosts(trie->getNds().size()); + + // skip the root node, init all to inf + for (size_t nid = 1; nid < trie->getNds().size(); nid++) { + costsDAG[nid].resize(ecm.at(nid).size(), DBL_INF); + predeDAG[nid].resize(ecm.at(nid).size(), NO_PREDE); + maxCosts.resize(ecm.at(nid).size(), 0); + } + + std::stack st; + + // init cost of all first childs + for (size_t cnid : trie->getNd(0).childs) { + st.push(cnid); + for (size_t frId = 0; frId < ecm.at(cnid).size(); frId++) { + costsDAG[cnid][frId] = ecm.at(cnid)[frId].pen; + } + } + + while (!st.empty()) { + size_t frTrNid = st.top(); + st.pop(); + const auto& frTrNd = trie->getNd(frTrNid); + for (size_t toTrNid : trie->getNd(frTrNid).childs) { + CostMatrix costM, dists; + const auto& toTrNd = trie->getNd(toTrNid); + + if (frTrNd.arr && !toTrNd.arr) { + for (size_t toId = 0; toId < costsDAG[toTrNid].size(); toId++) { + auto toCand = ecm.at(toTrNid)[toId]; + for (size_t frId : toCand.depPrede) { + double newC = costsDAG[frTrNid][frId] + ecm.at(toTrNid)[toId].pen; + if (newC < costsDAG[toTrNid][toId]) { + costsDAG[toTrNid][toId] = newC; + predeDAG[toTrNid][toId] = frId; + } + } + } + st.push(toTrNid); + continue; + } + + const double avgDepT = frTrNd.accTime / frTrNd.trips; + const double avgArrT = toTrNd.accTime / toTrNd.trips; + + double hopDist = 0; + + if (TW::NEED_DIST) + hopDist = util::geo::haversine(frTrNd.lat, frTrNd.lng, toTrNd.lat, + toTrNd.lng); + + uint32_t newMaxCost = TW::maxCost(avgArrT - avgDepT, rOpts); + uint32_t maxCost = newMaxCost; + + bool found = false; + int step = 0; + + while (!found && step <= MAX_ROUTE_COST_DOUBLING_STEPS) { + maxCosts[toTrNid] = newMaxCost; + maxCost = newMaxCost; + + // calculate n x n hops between layers + if (noFastHops || !TW::ALLOWS_FAST_ROUTE) { + hops(ecm.at(frTrNid), ecm.at(toTrNid), &costM, &dists, toTrNd.rAttrs, + rOpts, rest, hopCache, maxCost); + } else { + hopsFast(ecm.at(frTrNid), ecm.at(toTrNid), costsDAG[frTrNid], &costM, + toTrNd.rAttrs, rOpts, rest, hopCache, maxCost); + } + + for (size_t matrixI = 0; matrixI < costM.size(); matrixI++) { + const auto& mVal = costM[matrixI]; + const size_t frId = mVal.first.first; + const size_t toId = mVal.first.second; + const uint32_t c = mVal.second; + + double mDist = 0; + + // the dists and the costM matrices have entries at exactly the same + // loc + if (TW::NEED_DIST) mDist = dists[matrixI].second; + + // calculate the transition weights + const double depT = ecm.at(frTrNid)[frId].time; + const double arrT = ecm.at(toTrNid)[toId].time; + const double w = TW::weight(c, mDist, arrT - depT, hopDist, rOpts); + + // update costs to successors in next layer + double newC = costsDAG[frTrNid][frId] + ecm.at(toTrNid)[toId].pen + w; + if (newC < costsDAG[toTrNid][toId]) { + costsDAG[toTrNid][toId] = newC; + predeDAG[toTrNid][toId] = frId; + found = true; + } + } + + if (newMaxCost <= std::numeric_limits::max() / 2) + newMaxCost *= 2; + else + newMaxCost = std::numeric_limits::max(); + + if (newMaxCost == maxCost) break; + step++; + } + + if (!found) { + // write the cost for the NULL candidates as a fallback + for (size_t frNid = 0; frNid < ecm.at(frTrNid).size(); frNid++) { + double newC = costsDAG[frTrNid][frNid] + maxCost * 100; + // in the time expanded case, there might be multiple null cands + size_t nullCId = 0; + while (nullCId < ecm.at(toTrNid).size() && + !ecm.at(toTrNid)[nullCId].e) { + if (newC < costsDAG[toTrNid][nullCId]) { + predeDAG[toTrNid][nullCId] = frNid; + costsDAG[toTrNid][nullCId] = newC; + } + nullCId++; + } + } + + // for the remaining, write dummy edges + for (size_t frNid = 0; frNid < ecm.at(frTrNid).size(); frNid++) { + // skip NULL candidates + size_t toNid = 1; + while (toNid < ecm.at(toTrNid).size() && !ecm.at(toTrNid)[toNid].e) + toNid++; + for (; toNid < ecm.at(toTrNid).size(); toNid++) { + double newC = costsDAG[frTrNid][frNid] + ecm.at(toTrNid)[toNid].pen; + if (newC < costsDAG[toTrNid][toNid]) { + predeDAG[toTrNid][toNid] = frNid; + costsDAG[toTrNid][toNid] = newC; + } + } + } + } + + st.push(toTrNid); + } + } + + // update sink costs + std::unordered_map sinkCosts; + std::unordered_map frontIds; + for (auto leaf : trie->getNdTrips()) { + sinkCosts[leaf.first] = DBL_INF; + frontIds[leaf.first] = 0; + + for (size_t lastId = 0; lastId < ecm.at(leaf.first).size(); lastId++) { + double nCost = costsDAG[leaf.first][lastId]; + if (nCost < sinkCosts[leaf.first]) { + frontIds[leaf.first] = lastId; + sinkCosts[leaf.first] = nCost; + } + } + } + + // retrieve edges + for (auto leaf : trie->getNdTrips()) { + const auto leafNid = leaf.first; + auto curTrieNid = leafNid; + + while (predeDAG[curTrieNid][frontIds[leafNid]] != NO_PREDE) { + const auto curTrieParNid = trie->getNd(curTrieNid).parent; + const auto frId = predeDAG[curTrieNid][frontIds[leafNid]]; + const auto toId = frontIds[leafNid]; + + const auto frTrNd = trie->getNd(curTrieParNid); + const auto toTrNd = trie->getNd(curTrieNid); + + // skip in-node hops + if (frTrNd.arr && !toTrNd.arr) { + frontIds[leafNid] = frId; + curTrieNid = curTrieParNid; + continue; + } + + std::vector edgs; + + const auto& fr = ecm.at(curTrieParNid)[frId]; + const auto& to = ecm.at(curTrieNid)[toId]; + + // for subtracting and adding progression costs + typename TW::CostFunc costPr(toTrNd.rAttrs, rOpts, rest, ROUTE_INF); + + if (fr.e && to.e) { + // account for max progression start offset, do this exactly like + // in the hops calculation to ensure that we can find the path again + double maxProgrStart = 0; + for (const auto& fr : ecm.at(curTrieParNid)) { + if (!fr.e) continue; + double progrStart = 0; + if (fr.progr > 0) progrStart = costPr(fr.e, 0, 0) * fr.progr; + if (progrStart > maxProgrStart) maxProgrStart = progrStart; + } + + const double maxCostRt = maxCosts[curTrieNid] + maxProgrStart; + uint32_t maxCostRtInt = maxCostRt; + + // avoid overflow + if (maxCostRt >= std::numeric_limits::max()) { + maxCostRtInt = std::numeric_limits::max(); + } + + typename TW::CostFunc cost(toTrNd.rAttrs, rOpts, rest, maxCostRtInt); + typename TW::DistHeur distH(fr.e->getFrom()->pl().getComp().maxSpeed, + rOpts, {to.e}); + + const double c = + EDijkstra::shortestPath(fr.e, to.e, cost, distH, &edgs); + // c += costPr(to.e, 0, 0) * to.progr; + + if (c < maxCostRtInt) { + // a path was found, use it + ret[leafNid].push_back( + {edgs, fr.e, to.e, fr.progr, to.progr, {}, {}}); + } else { + // no path was found, which is marked by an empty edge list + ret[leafNid].push_back({{}, fr.e, to.e, fr.progr, to.progr, {}, {}}); + } + } else { + // fallback to the position given in candidate + if (fr.e) { + ret[leafNid].push_back({edgs, fr.e, 0, fr.progr, 0, {}, to.point}); + } else if (to.e) { + ret[leafNid].push_back({edgs, 0, to.e, 0, to.progr, fr.point, {}}); + } else { + ret[leafNid].push_back({edgs, 0, 0, 0, 0, fr.point, to.point}); + } + } + frontIds[leafNid] = frId; + curTrieNid = curTrieParNid; + } + } + + return ret; +} + +// _____________________________________________________________________________ +template +void RouterImpl::hops(const EdgeCandGroup& froms, const EdgeCandGroup& tos, + CostMatrix* rCosts, CostMatrix* dists, + const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& rest, HopCache* hopCache, + uint32_t maxCost) const { + // standard 1 -> n approach + std::set eFrs; + for (const auto& from : froms) { + if (!from.e) continue; + eFrs.insert(from.e); + } + + std::set eTos; + for (const auto& to : tos) { + if (!to.e) continue; + eTos.insert(to.e); + } + + EdgeCostMatrix ecm; + EdgeDistMatrix ecmDist; + + // account for max progression start offset + double maxProgrStart = 0; + typename TW::CostFunc cost(rAttrs, rOpts, rest, ROUTE_INF); + for (const auto& fr : froms) { + if (!fr.e) continue; + double progrStart = 0; + if (fr.progr > 0) progrStart = cost(fr.e, 0, 0) * fr.progr; + if (progrStart > maxProgrStart) maxProgrStart = progrStart; + } + + maxCost = addNonOverflow(maxCost, maxProgrStart); + typename TW::CostFunc costF(rAttrs, rOpts, rest, maxCost); + + for (trgraph::Edge* eFrom : eFrs) { + std::set remTos; + for (trgraph::Edge* eTo : eTos) { + // init ecmDist + ecmDist[eFrom][eTo] = ROUTE_INF; + + std::pair cached = {0, 0}; + if (hopCache) cached = hopCache->get(eFrom, eTo); + + // shortcut: if the nodes lie in two different connected components, + // the distance between them is trivially infinite + if (eFrom->getFrom()->pl().getCompId() != + eTo->getTo()->pl().getCompId()) { + ecm[eFrom][eTo] = costF.inf(); + } else if (cached.second >= costF.inf()) { + ecm[eFrom][eTo] = costF.inf(); + } else if (!TW::NEED_DIST && cached.second) { + ecm[eFrom][eTo] = cached.first; + } else { + remTos.insert(eTo); + } + } + + if (remTos.size()) { + typename TW::DistHeur distH(eFrom->getFrom()->pl().getComp().maxSpeed, + rOpts, remTos); + + std::unordered_map paths; + std::unordered_map pathPtrs; + for (auto to : tos) pathPtrs[to.e] = &paths[to.e]; + + const auto& costs = + EDijkstra::shortestPath(eFrom, remTos, costF, distH, pathPtrs); + + for (const auto& c : costs) { + ecm[eFrom][c.first] = c.second; + + if (paths[c.first].size() == 0) { + if (hopCache) hopCache->setMin(eFrom, c.first, maxCost); + continue; // no path found + } + + if (hopCache) hopCache->setEx(eFrom, c.first, c.second); + } + + if (TW::NEED_DIST) { + for (const auto& c : costs) { + if (!paths[c.first].size()) continue; + double d = 0; + // don't count last edge + for (size_t i = paths[c.first].size() - 1; i > 0; i--) { + d += paths[c.first][i]->pl().getLength(); + } + ecmDist[eFrom][c.first] = d; + } + } + } + } + + // build return costs + for (size_t frId = 0; frId < froms.size(); frId++) { + auto fr = froms[frId]; + if (!fr.e) continue; + auto costFr = costF(fr.e, 0, 0); + for (size_t toId = 0; toId < tos.size(); toId++) { + auto to = tos[toId]; + if (!to.e) continue; + auto costTo = costF(to.e, 0, 0); + + uint32_t c = ecm[fr.e][to.e]; + + if (c >= maxCost) continue; + + double dist = 0; + if (TW::NEED_DIST) dist = ecmDist[fr.e][to.e]; + + if (fr.e == to.e) { + if (fr.progr <= to.progr) { + const uint32_t progrCFr = costFr * fr.progr; + const uint32_t progrCTo = costTo * to.progr; + + // calculate this in one step to avoid uint32_t underflow below + c += progrCTo - progrCFr; + } else { + // trivial case we can ignore + continue; + } + + } else { + // subtract progression cost on first edge + if (fr.progr > 0) { + const uint32_t progrCFr = costFr * fr.progr; + c -= progrCFr; + if (TW::NEED_DIST) dist -= fr.e->pl().getLength() * fr.progr; + } + + // add progression cost on last edge + if (to.progr > 0) { + const uint32_t progrCTo = costTo * to.progr; + c += progrCTo; + if (TW::NEED_DIST) dist += to.e->pl().getLength() * to.progr; + } + } + + if (c < maxCost) { + rCosts->push_back({{frId, toId}, c}); + if (TW::NEED_DIST) dists->push_back({{frId, toId}, dist}); + } + } + } +} + +// _____________________________________________________________________________ +template +void RouterImpl::hopsFast(const EdgeCandGroup& froms, + const EdgeCandGroup& tos, + const LayerCostsDAG& rawInitCosts, + CostMatrix* rCosts, const RoutingAttrs& rAttrs, + const RoutingOpts& rOpts, + const osm::Restrictor& restr, HopCache* hopCache, + uint32_t maxCost) const { + std::unordered_map initCosts; + + std::set eFrs, eTos; + std::map> eFrCands, eToCands; + double maxSpeed = 0; + for (size_t frId = 0; frId < froms.size(); frId++) { + if (rawInitCosts[frId] >= DBL_INF || !connected(froms[frId], tos)) continue; + + eFrs.insert(froms[frId].e); + eFrCands[froms[frId].e].push_back(frId); + + if (froms[frId].e->getFrom()->pl().getComp().maxSpeed > maxSpeed) + maxSpeed = froms[frId].e->getFrom()->pl().getComp().maxSpeed; + } + + for (size_t toId = 0; toId < tos.size(); toId++) { + if (!connected(froms, tos[toId])) + continue; // skip nodes not conn'ed to any + + if (hopCache && cacheDrop(hopCache, eFrs, tos[toId].e, maxCost)) + continue; // skip nodes we have already encountered at higher cost + + eTos.insert(tos[toId].e); + eToCands[tos[toId].e].push_back(toId); + } + + if (eFrs.size() == 0 || eTos.size() == 0) return; + + // account for max progression start offset + double maxProgrStart = 0; + typename TW::CostFunc progrCostF(rAttrs, rOpts, restr, ROUTE_INF); + for (const auto& fr : froms) { + if (!fr.e) continue; + double progrStart = 0; + if (fr.progr > 0) progrStart = progrCostF(fr.e, 0, 0) * fr.progr; + if (progrStart > maxProgrStart) maxProgrStart = progrStart; + } + + // initialize init doubles + LayerCostsDAG prepInitCosts(froms.size()); + for (size_t frId = 0; frId < froms.size(); frId++) { + if (!froms[frId].e || rawInitCosts[frId] >= DBL_INF) continue; + const auto& fr = froms[frId]; + // offset by progr start + double progrStart = progrCostF(fr.e, 0, 0) * fr.progr; + prepInitCosts[frId] = + TW::invWeight(rawInitCosts[frId], rOpts) + maxProgrStart - progrStart; + } + + // all init costs are inf + for (const auto& fr : froms) initCosts[fr.e] = ROUTE_INF; + + // now chose the best offset cost + for (size_t frId = 0; frId < froms.size(); frId++) { + if (!froms[frId].e || rawInitCosts[frId] >= DBL_INF) continue; + const auto& fr = froms[frId]; + if (prepInitCosts[frId] < initCosts[fr.e]) + initCosts[fr.e] = prepInitCosts[frId]; + } + + // get max init costs + uint32_t maxInit = 0; + uint32_t minInit = ROUTE_INF; + for (const auto& c : initCosts) { + if (!eFrs.count(c.first)) continue; + if (c.second != ROUTE_INF && c.second > maxInit) maxInit = c.second; + if (c.second < minInit) minInit = c.second; + } + + for (auto& c : initCosts) c.second = c.second - minInit; + + // account for start offsets + maxCost = addNonOverflow(maxCost, maxProgrStart); + + typename TW::CostFunc costF(rAttrs, rOpts, restr, + maxCost + (maxInit - minInit)); + + std::unordered_map paths; + std::unordered_map pathPtrs; + for (const auto& to : tos) pathPtrs[to.e] = &paths[to.e]; + + typename TW::DistHeur distH(maxSpeed, rOpts, eTos); + + const auto& costs = + EDijkstra::shortestPath(eFrs, eTos, initCosts, maxCost, costF, distH); + + for (const auto& c : costs) { + auto toEdg = c.first; + if (c.second.second >= costF.inf()) { + if (hopCache) hopCache->setMin(eFrs, toEdg, maxCost); + continue; // no path found + } + auto fromEdg = c.second.first; + uint32_t cost = c.second.second - initCosts[fromEdg]; + + if (cost >= maxCost) continue; + + for (size_t frId : eFrCands.find(fromEdg)->second) { + const auto& fr = froms[frId]; + auto costFr = costF(fr.e, 0, 0); + + for (size_t toId : eToCands.find(toEdg)->second) { + const auto& to = tos[toId]; + uint32_t wrCost = cost; + + if (fr.e == to.e) { + if (fr.progr <= to.progr) { + const auto costTo = costF(to.e, 0, 0); + const uint32_t progrCFr = costFr * fr.progr; + const uint32_t progrCTo = costTo * to.progr; + + // calculate this in one step to avoid uint32_t underflow below + wrCost += progrCTo - progrCFr; + } else { + // trivial case we can ignore + continue; + } + } else { + // subtract progression cost on first edge + if (fr.progr > 0) { + const uint32_t progrCFr = costFr * fr.progr; + wrCost -= progrCFr; + } + + // add progression cost on last edge + if (to.progr > 0) { + const auto costTo = costF(to.e, 0, 0); + const uint32_t progrCTo = costTo * to.progr; + wrCost += progrCTo; + } + } + + if (wrCost >= maxCost - maxProgrStart) continue; + + rCosts->push_back({{frId, toId}, wrCost}); + } + } + } +} + +// _____________________________________________________________________________ +template +bool RouterImpl::connected(const EdgeCand& fr, + const EdgeCandGroup& tos) const { + if (!fr.e) return false; + for (const auto& to : tos) { + if (!to.e) continue; + if (fr.e->getFrom()->pl().getCompId() == to.e->getFrom()->pl().getCompId()) + return true; + } + return false; +} + +// _____________________________________________________________________________ +template +bool RouterImpl::connected(const EdgeCandGroup& froms, + const EdgeCand& to) const { + if (!to.e) return false; + for (const auto& fr : froms) { + if (!fr.e) continue; + if (fr.e->getFrom()->pl().getCompId() == to.e->getFrom()->pl().getCompId()) + return true; + } + return false; +} + +// _____________________________________________________________________________ +template +bool RouterImpl::cacheDrop(HopCache* hopCache, + const std::set& froms, + const trgraph::Edge* to, + uint32_t maxCost) const { + for (auto fr : froms) + if (hopCache->get(fr, to).first <= maxCost) return false; + + return true; +} + +// _____________________________________________________________________________ +template +uint32_t RouterImpl::addNonOverflow(uint32_t a, uint32_t b) const { + if (a == std::numeric_limits::max() || + b == std::numeric_limits::max()) + return std::numeric_limits::max(); + uint32_t res = a + b; + if (res >= a && res >= b) return res; + return std::numeric_limits::max(); +} diff --git a/src/pfaedle/router/RoutingAttrs.h b/src/pfaedle/router/RoutingAttrs.h index 11a5cdb..c1b001f 100644 --- a/src/pfaedle/router/RoutingAttrs.h +++ b/src/pfaedle/router/RoutingAttrs.h @@ -5,8 +5,10 @@ #ifndef PFAEDLE_ROUTER_ROUTINGATTRS_H_ #define PFAEDLE_ROUTER_ROUTINGATTRS_H_ -#include +#include +#include #include +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "pfaedle/trgraph/EdgePL.h" using pfaedle::trgraph::TransitEdgeLine; @@ -14,40 +16,74 @@ using pfaedle::trgraph::TransitEdgeLine; namespace pfaedle { namespace router { +struct LineSimilarity { + bool nameSimilar : 1; + bool fromSimilar : 1; + bool toSimilar : 1; +}; + +inline bool operator<(const LineSimilarity& a, const LineSimilarity& b) { + return (a.nameSimilar + a.fromSimilar + a.toSimilar) < + (b.nameSimilar + b.fromSimilar + b.toSimilar); +} + struct RoutingAttrs { - RoutingAttrs() : fromString(""), toString(""), shortName(""), _simiCache() {} - std::string fromString; - std::string toString; + RoutingAttrs() + : lineFrom(""), lineTo(), shortName(""), classifier(0), _simiCache() {} + std::string lineFrom; + std::vector lineTo; std::string shortName; - mutable std::map _simiCache; + const pfaedle::statsimiclassifier::StatsimiClassifier* classifier; + + mutable std::unordered_map _simiCache; + + LineSimilarity simi(const TransitEdgeLine* line) const { + // shortcut, if we don't have a line information, classify as similar + if (line->shortName.empty() && line->toStr.empty() && line->fromStr.empty()) + return {true, true, true}; - // carfull: lower return value = higher similarity - double simi(const TransitEdgeLine* line) const { auto i = _simiCache.find(line); if (i != _simiCache.end()) return i->second; - double cur = 1; + LineSimilarity ret{false, false, false}; + if (shortName.empty() || router::lineSimi(line->shortName, shortName) > 0.5) - cur -= 0.333333333; + ret.nameSimilar = true; - if (toString.empty() || line->toStr.empty() || - router::statSimi(line->toStr, toString) > 0.5) - cur -= 0.333333333; + if (lineTo.size() == 0) { + ret.toSimilar = true; + } else { + for (const auto& lTo : lineTo) { + if (lTo.empty() || classifier->similar(line->toStr, lTo)) { + ret.toSimilar = true; + break; + } + } + } - if (fromString.empty() || line->fromStr.empty() || - router::statSimi(line->fromStr, fromString) > 0.5) - cur -= 0.333333333; + if (lineFrom.empty() || classifier->similar(line->fromStr, lineFrom)) + ret.fromSimilar = true; - _simiCache[line] = cur; + _simiCache[line] = ret; - return cur; + return ret; + } + + void merge(const RoutingAttrs& other) { + assert(other.lineFrom == lineFrom); + assert(other.shortName == shortName); + + for (const auto& l : other.lineTo) { + auto i = std::lower_bound(lineTo.begin(), lineTo.end(), l); + if (i != lineTo.end() && (*i) == l) continue; // already present + lineTo.insert(i, l); + } } }; inline bool operator==(const RoutingAttrs& a, const RoutingAttrs& b) { - return a.shortName == b.shortName && a.toString == b.toString && - a.fromString == b.fromString; + return a.shortName == b.shortName && a.lineFrom == b.lineFrom; } inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) { @@ -55,10 +91,8 @@ inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) { } inline bool operator<(const RoutingAttrs& a, const RoutingAttrs& b) { - return a.fromString < b.fromString || - (a.fromString == b.fromString && a.toString < b.toString) || - (a.fromString == b.fromString && a.toString == b.toString && - a.shortName < b.shortName); + return a.lineFrom < b.lineFrom || + (a.lineFrom == b.lineFrom && a.shortName < b.shortName); } } // namespace router diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index cd5c85b..6223cfe 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -2,27 +2,23 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi -#ifdef _OPENMP -#include -#else -#define omp_get_thread_num() 0 -#define omp_get_num_procs() 1 -#endif - -#include +#include +#include +#include #include #include +#include #include +#include #include #include #include "ad/cppgtfs/gtfs/Feed.h" #include "pfaedle/Def.h" -#include "pfaedle/eval/Collector.h" #include "pfaedle/gtfs/Feed.h" #include "pfaedle/gtfs/StopTime.h" #include "pfaedle/osm/OsmBuilder.h" #include "pfaedle/router/ShapeBuilder.h" -#include "pfaedle/trgraph/StatGroup.h" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "util/geo/Geo.h" #include "util/geo/output/GeoGraphJsonOutput.h" #include "util/geo/output/GeoJsonOutput.h" @@ -33,255 +29,515 @@ using util::geo::DBox; using util::geo::DPoint; using util::geo::extendBox; using util::geo::minbox; +using util::geo::PolyLine; +using ad::cppgtfs::gtfs::NO_COLOR; using ad::cppgtfs::gtfs::ShapePoint; using ad::cppgtfs::gtfs::Stop; using pfaedle::gtfs::Feed; using pfaedle::gtfs::StopTime; using pfaedle::gtfs::Trip; using pfaedle::osm::BBoxIdx; -using pfaedle::router::Clusters; +using pfaedle::router::EdgeCandGroup; +using pfaedle::router::EdgeCandMap; using pfaedle::router::EdgeListHops; using pfaedle::router::FeedStops; -using pfaedle::router::NodeCandGroup; -using pfaedle::router::NodeCandRoute; using pfaedle::router::RoutingAttrs; using pfaedle::router::ShapeBuilder; +using pfaedle::router::Stats; +using pfaedle::router::TripForests; +using pfaedle::router::TripTrie; +using pfaedle::statsimiclassifier::JaccardClassifier; +using pfaedle::trgraph::EdgeGrid; +using pfaedle::trgraph::NodeGrid; using util::geo::latLngToWebMerc; -using util::geo::webMercMeterDist; -using util::geo::webMercToLatLng; +using util::geo::M_PER_DEG; using util::geo::output::GeoGraphJsonOutput; // _____________________________________________________________________________ -ShapeBuilder::ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, - MOTs mots, const config::MotConfig& motCfg, - eval::Collector* ecoll, pfaedle::trgraph::Graph* g, - router::FeedStops* fStops, osm::Restrictor* restr, - const config::Config& cfg) +ShapeBuilder::ShapeBuilder( + Feed* feed, MOTs mots, const config::MotConfig& motCfg, + pfaedle::trgraph::Graph* g, router::FeedStops* fStops, + osm::Restrictor* restr, + const pfaedle::statsimiclassifier::StatsimiClassifier* classifier, + router::Router* router, const config::Config& cfg) : _feed(feed), - _evalFeed(evalFeed), _mots(mots), _motCfg(motCfg), - _ecoll(ecoll), _cfg(cfg), _g(g), - _crouter(omp_get_num_procs(), cfg.useCaching), _stops(fStops), _curShpCnt(0), - _restr(restr) { - _numThreads = _crouter.getCacheNumber(); + _restr(restr), + _classifier(classifier), + _router(router) { + pfaedle::osm::BBoxIdx box(BOX_PADDING); + ShapeBuilder::getGtfsBox(feed, mots, cfg.shapeTripId, cfg.dropShapes, &box, + _motCfg.osmBuildOpts.maxSpeed); + + _eGrid = EdgeGrid(cfg.gridSize, cfg.gridSize, box.getFullBox(), false); + _nGrid = NodeGrid(cfg.gridSize, cfg.gridSize, box.getFullBox(), false); + + LOG(DEBUG) << "Grid size of " << _nGrid.getXWidth() << "x" + << _nGrid.getYHeight(); + + buildIndex(); } // _____________________________________________________________________________ -const NodeCandGroup& ShapeBuilder::getNodeCands(const Stop* s) const { - if (_stops->find(s) == _stops->end() || _stops->at(s) == 0) return _emptyNCG; - return _stops->at(s)->pl().getSI()->getGroup()->getNodeCands(s); +void ShapeBuilder::buildIndex() { + for (auto* n : _g->getNds()) { + for (auto* e : n->getAdjListOut()) { + if (e->pl().lvl() > _motCfg.osmBuildOpts.maxSnapLevel) continue; + // don't snap to one way edges + if (e->pl().oneWay() == 2) continue; + + _eGrid.add(*e->pl().getGeom(), e); + } + } + + for (auto* n : _g->getNds()) { + // only station nodes + if (n->pl().getSI()) { + _nGrid.add(*n->pl().getGeom(), n); + } + } } // _____________________________________________________________________________ -LINE ShapeBuilder::shapeL(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs) { - try { - const router::EdgeListHops& res = route(ncr, rAttrs); +void ShapeBuilder::buildCandCache(const TripForests& forests) { + std::set stops; + size_t count = 0; - LINE l; - for (const auto& hop : res) { - const trgraph::Node* last = hop.start; - if (hop.edges.size() == 0) { - l.push_back(*hop.start->pl().getGeom()); - l.push_back(*hop.end->pl().getGeom()); - } - for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) { - const auto* e = *i; - if ((e->getFrom() == last) ^ e->pl().isRev()) { - l.insert(l.end(), e->pl().getGeom()->begin(), - e->pl().getGeom()->end()); - } else { - l.insert(l.end(), e->pl().getGeom()->rbegin(), - e->pl().getGeom()->rend()); + for (const auto& forest : forests) { + for (const auto& trie : forest.second) { + for (const auto& trips : trie.getNdTrips()) { + for (const auto& st : trips.second[0]->getStopTimes()) { + stops.insert(st.getStop()); } - last = e->getOtherNd(last); } } + } - return l; + size_t numThreads = std::thread::hardware_concurrency(); + std::vector thrds(numThreads); + std::vector caches(numThreads); + std::vector> threadStops(numThreads); + + size_t i = 0; + for (auto stop : stops) { + threadStops[i].push_back(stop); + if (++i == numThreads) i = 0; + } + + i = 0; + for (auto& t : thrds) { + t = std::thread(&ShapeBuilder::edgCandWorker, this, &threadStops[i], + &caches[i]); + i++; + } + + for (auto& thr : thrds) thr.join(); + + // merge + for (size_t i = 0; i < numThreads; i++) { + for (const auto& c : caches[i]) { + _grpCache[c.first] = c.second; + count += c.second.size(); + } + } + + if (_grpCache.size()) + LOG(DEBUG) << "Average candidate set size: " + << ((count * 1.0) / _grpCache.size()); +} + +// _____________________________________________________________________________ +EdgeCandGroup ShapeBuilder::getEdgCands(const Stop* s) const { + auto cached = _grpCache.find(s); + if (cached != _grpCache.end()) return cached->second; + + EdgeCandGroup ret; + + const auto& snormzer = _motCfg.osmBuildOpts.statNormzer; + auto normedName = snormzer.norm(s->getName()); + + // the first cand is a placeholder for the stop position itself, it is chosen + // when no candidate yielded a feasible route + auto pos = POINT(s->getLng(), s->getLat()); + ret.push_back({0, 0, 0, pos, 0, {}}); + + // unsigned seed = + // std::chrono::system_clock::now().time_since_epoch().count(); + // std::default_random_engine gen(seed); + // std::normal_distribution dist(0.0, 25.0); + + // add some gaussian noise + // pos.setX(pos.getX() + dist(gen)); + // pos.setY(pos.getY() + dist(gen)); + + double maxMDist = _motCfg.osmBuildOpts.maxStationCandDistance; + + double distor = util::geo::latLngDistFactor(pos); + + std::set frNIdx; + _nGrid.get(util::geo::pad(util::geo::getBoundingBox(pos), + (maxMDist / M_PER_DEG) / distor), + &frNIdx); + + if (_motCfg.routingOpts.useStations) { + for (auto nd : frNIdx) { + assert(nd->pl().getSI()); + + double mDist = util::geo::haversine(pos, *nd->pl().getGeom()); + if (mDist > maxMDist) continue; + + double nameMatchPunish = 0; + double trackMatchPunish = 0; + + if (!_classifier->similar(normedName, pos, nd->pl().getSI()->getName(), + *nd->pl().getGeom())) { + // stations do not match, punish + nameMatchPunish = _motCfg.routingOpts.stationUnmatchedPen; + } + + std::string platform = s->getPlatformCode(); + + if (!platform.empty() && !nd->pl().getSI()->getTrack().empty() && + nd->pl().getSI()->getTrack() == platform) { + trackMatchPunish = _motCfg.routingOpts.platformUnmatchedPen; + } + + for (auto* e : nd->getAdjListOut()) { + // don't snap to one way edges + if (e->pl().oneWay() == 2) continue; + ret.push_back({e, + mDist * _motCfg.routingOpts.stationDistPenFactor + + nameMatchPunish + trackMatchPunish, + 0, + {}, + 0, + {}}); + } + } + } + + maxMDist = _motCfg.osmBuildOpts.maxSnapDistance; + + std::set frEIdx; + _eGrid.get(util::geo::pad(util::geo::getBoundingBox(pos), + (maxMDist / M_PER_DEG) / distor), + &frEIdx); + + std::set selected; + std::map scores; + std::map progrs; + + for (auto edg : frEIdx) { + if (selected.count(edg)) continue; + + auto reach = deg2reachable(edg, selected); + + double mDist = dist(pos, *edg->pl().getGeom()) * distor * M_PER_DEG; + + if (mDist > maxMDist) continue; + + if (!reach || mDist < scores[reach]) { + if (reach) { + selected.erase(selected.find(reach)); + scores.erase(scores.find(reach)); + } + util::geo::PolyLine pl(*edg->pl().getGeom()); + auto lp = pl.projectOn(pos); + double progr = lp.totalPos; + if (edg->pl().isRev()) progr = 1 - progr; + selected.insert(edg); + scores[edg] = mDist; + progrs[edg] = progr; + } + } + + for (auto e : selected) { + ret.push_back({e, + scores[e] * _motCfg.routingOpts.stationDistPenFactor + + _motCfg.routingOpts.nonStationPen, + progrs[e], + {}, + 0, + {}}); + } + + return ret; +} + +// _____________________________________________________________________________ +pfaedle::trgraph::Edge* ShapeBuilder::deg2reachable( + trgraph::Edge* e, std::set edgs) const { + trgraph::Edge* cur = e; + + // forward + while (cur->getTo()->getDeg() == 2) { + // dont allow backtracking on reverse edge + auto next = e->getTo()->getAdjListOut().front()->getTo() == e->getFrom() + ? e->getTo()->getAdjListOut().back() + : e->getTo()->getAdjListOut().front(); + if (next == e || next == cur) break; // avoid circles + if (next->pl().oneWay() == 2) break; // dont follow one way edges + if (edgs.count(next)) return next; + cur = next; + } + + // backward + while (cur->getFrom()->getDeg() == 2) { + // dont allow backtracking on reverse edge + auto next = e->getFrom()->getAdjListIn().front()->getFrom() == e->getTo() + ? e->getFrom()->getAdjListIn().back() + : e->getFrom()->getAdjListIn().front(); + if (next == e || next == cur) break; // avoid circles + if (next->pl().oneWay() == 2) break; // dont follow one way edges + if (edgs.count(cur)) return next; + cur = next; + } + + return 0; +} + +// _____________________________________________________________________________ +std::pair, Stats> ShapeBuilder::shapeL(Trip* trip) { + Stats stats; + try { + T_START(t); + EDijkstra::ITERS = 0; + auto hops = shapeify(trip); + stats.solveTime = T_STOP(t); + stats.numTries = 1; + stats.numTrieLeafs = 1; + stats.totNumTrips = 1; + stats.dijkstraIters = EDijkstra::ITERS; + std::map colors; + LOG(INFO) << "Matched 1 trip in " << stats.solveTime << " ms."; + // print to line + return {getGeom(hops, getRAttrs(trip), &colors), stats}; } catch (const std::runtime_error& e) { LOG(ERROR) << e.what(); - return LINE(); + return {std::vector(), stats}; } } // _____________________________________________________________________________ -LINE ShapeBuilder::shapeL(Trip* trip) { - return shapeL(getNCR(trip), getRAttrs(trip)); +std::map ShapeBuilder::route( + const TripTrie* trie, const EdgeCandMap& ecm, HopCache* hopCache) const { + return _router->route(trie, ecm, _motCfg.routingOpts, *_restr, hopCache, + _cfg.noFastHops); } // _____________________________________________________________________________ -EdgeListHops ShapeBuilder::route(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs) const { - router::Graph g; +std::map ShapeBuilder::shapeify( + const TripTrie* trie, HopCache* hopCache) const { + LOG(VDEBUG) << "Map-matching trie " << trie; - if (_cfg.solveMethod == "global") { - const router::EdgeListHops& ret = - _crouter.route(ncr, rAttrs, _motCfg.routingOpts, *_restr, &g); + // TODO(patrick): assumes the trie is not empty, check this! + assert(trie->getNdTrips().size()); + assert(trie->getNdTrips().begin()->second.size()); + RoutingAttrs rAttrs = getRAttrs(trie->getNdTrips().begin()->second[0]); - // write combination graph - if (!_cfg.shapeTripId.empty() && _cfg.writeCombGraph) { - LOG(INFO) << "Outputting combgraph.json..."; - std::ofstream pstr(_cfg.dbgOutputPath + "/combgraph.json"); - GeoGraphJsonOutput o; - o.printLatLng(g, pstr); - } + std::map ret; - return ret; - } else if (_cfg.solveMethod == "greedy") { - return _crouter.routeGreedy(ncr, rAttrs, _motCfg.routingOpts, *_restr); - } else if (_cfg.solveMethod == "greedy2") { - return _crouter.routeGreedy2(ncr, rAttrs, _motCfg.routingOpts, *_restr); - } else { - LOG(ERROR) << "Unknown solution method " << _cfg.solveMethod; - exit(1); + const auto& routes = route(trie, getECM(trie), hopCache); + + for (const auto& route : routes) { + ret[route.first] = route.second; } - return EdgeListHops(); -} - -// _____________________________________________________________________________ -pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) const { - LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot " - << trip->getRoute()->getType() << "(sn=" << trip->getShortname() - << ", rsn=" << trip->getRoute()->getShortName() - << ", rln=" << trip->getRoute()->getLongName() << ")"; - Shape ret; - ret.hops = route(getNCR(trip), getRAttrs(trip)); - ret.avgHopDist = avgHopDist(trip); - - LOG(VDEBUG) << "Finished map-matching for #" << trip->getId(); + LOG(VDEBUG) << "Finished map-matching for trie " << trie; return ret; } // _____________________________________________________________________________ -pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) { +EdgeListHops ShapeBuilder::shapeify(Trip* trip) { LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot " << trip->getRoute()->getType() << "(sn=" << trip->getShortname() << ", rsn=" << trip->getRoute()->getShortName() << ", rln=" << trip->getRoute()->getLongName() << ")"; + TripTrie trie; + trie.addTrip(trip, getRAttrs(trip), + _motCfg.routingOpts.transPenMethod == "timenorm", false); + const auto& routes = route(&trie, getECM(&trie), 0); - Shape ret; - ret.hops = route(getNCR(trip), getRAttrs(trip)); - ret.avgHopDist = avgHopDist(trip); - - LOG(VDEBUG) << "Finished map-matching for #" << trip->getId(); - - return ret; + return routes.begin()->second; } // _____________________________________________________________________________ -void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) { - TrGraphEdgs gtfsGraph; +Stats ShapeBuilder::shapeify(pfaedle::netgraph::Graph* outNg) { + Stats stats; + EDijkstra::ITERS = 0; + T_START(cluster); LOG(DEBUG) << "Clustering trips..."; - Clusters clusters = clusterTrips(_feed, _mots); - LOG(DEBUG) << "Clustered trips into " << clusters.size() << " clusters."; + const TripForests& forests = clusterTrips(_feed, _mots); + for (const auto& forest : forests) { + for (const auto& trie : forest.second) { + stats.numTries++; + stats.numTrieLeafs += trie.getNdTrips().size(); + } + } + LOG(DEBUG) << "Clustered trips into " << stats.numTries + << " tries with a total of " << stats.numTrieLeafs << " leafs in " + << T_STOP(cluster) << "ms"; + + LOG(DEBUG) << "Building candidate cache..."; + buildCandCache(forests); + LOG(DEBUG) << "Done."; + + std::map shpUse; + RouteRefColors refColors; - std::map shpUsage; for (auto t : _feed->getTrips()) { - if (!t.getShape().empty()) shpUsage[t.getShape()]++; - } + if (!t.getShape().empty()) shpUse[t.getShape()]++; - // to avoid unfair load balance on threads - std::random_shuffle(clusters.begin(), clusters.end()); + // write the colors of trips we won't touch, but whose route we might + if (t.getStopTimes().size() < 2) continue; + if (!_mots.count(t.getRoute()->getType()) || + !_motCfg.mots.count(t.getRoute()->getType())) + continue; - size_t iters = EDijkstra::ITERS; - size_t totiters = EDijkstra::ITERS; - size_t oiters = EDijkstra::ITERS; - size_t j = 0; - - auto t1 = TIME(); - auto t2 = TIME(); - double totAvgDist = 0; - size_t totNumTrips = 0; - -#pragma omp parallel for num_threads(_numThreads) - for (size_t i = 0; i < clusters.size(); i++) { - j++; - - if (j % 10 == 0) { -#pragma omp critical - { - LOG(INFO) << "@ " << j << " / " << clusters.size() << " (" - << (static_cast((j * 1.0) / clusters.size() * 100)) - << "%, " << (EDijkstra::ITERS - oiters) << " iters, " - << "matching " << (10.0 / (TOOK(t1, TIME()) / 1000)) - << " trips/sec)"; - - oiters = EDijkstra::ITERS; - t1 = TIME(); - } - } - - // explicitly call const version of shape here for thread safety - const Shape& cshp = - const_cast(*this).shape(clusters[i][0]); - totAvgDist += cshp.avgHopDist; - - if (_cfg.buildTransitGraph) { -#pragma omp critical - { writeTransitGraph(cshp, >fsGraph, clusters[i]); } - } - - std::vector distances; - const ad::cppgtfs::gtfs::Shape& shp = - getGtfsShape(cshp, clusters[i][0], &distances); - - LOG(VDEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations."; - iters = EDijkstra::ITERS; - - totNumTrips += clusters[i].size(); - - for (auto t : clusters[i]) { - if (_cfg.evaluate && _evalFeed && _ecoll) { - std::lock_guard guard(_shpMutex); - _ecoll->add(t, _evalFeed->getShapes().get(t->getShape()), shp, - distances); - } - - if (!t->getShape().empty() && shpUsage[t->getShape()] > 0) { - shpUsage[t->getShape()]--; - if (shpUsage[t->getShape()] == 0) { - std::lock_guard guard(_shpMutex); - _feed->getShapes().remove(t->getShape()); - } - } - setShape(t, shp, distances); + if (!t.getShape().empty() && !_cfg.dropShapes) { + refColors[t.getRoute()][t.getRoute()->getColor()].push_back(&t); } } - LOG(INFO) << "Matched " << totNumTrips << " trips in " << clusters.size() - << " clusters."; - LOG(DEBUG) << "Took " << (EDijkstra::ITERS - totiters) - << " iterations in total."; - LOG(DEBUG) << "Took " << TOOK(t2, TIME()) << " ms in total."; - LOG(DEBUG) << "Total avg. tput " - << (static_cast(EDijkstra::ITERS - totiters)) / - TOOK(t2, TIME()) - << " iters/sec"; - LOG(DEBUG) << "Total avg. trip tput " - << (clusters.size() / (TOOK(t2, TIME()) / 1000)) << " trips/sec"; - LOG(DEBUG) << "Avg hop distance was " - << (totAvgDist / static_cast(clusters.size())) - << " meters"; + // we implicitely cluster by routing attrs here. This ensures that now two + // threads will access the same routing attrs later on, which safes us an + // expensive locking mechanism later on for the hop cache + std::vector tries; + for (const auto& forest : forests) { + tries.push_back(&(forest.second)); + for (const auto& trie : forest.second) { + for (const auto& trips : trie.getNdTrips()) { + stats.totNumTrips += trips.second.size(); + } + } + } + + auto tStart = TIME(); + std::atomic at(0); + + size_t numThreads = std::thread::hardware_concurrency(); + std::vector thrds(numThreads); + std::vector colors(numThreads); + std::vector gtfsGraphs(numThreads); + + size_t i = 0; + for (auto& t : thrds) { + t = std::thread(&ShapeBuilder::shapeWorker, this, &tries, &at, &shpUse, + &colors[i], >fsGraphs[i]); + i++; + } + + for (auto& thr : thrds) thr.join(); + + stats.solveTime = TOOK(tStart, TIME()); + + LOG(INFO) << "Matched " << stats.totNumTrips << " trips in " + << stats.solveTime << " ms."; + + // merge colors + for (auto& cols : colors) { + for (auto& route : cols) { + for (auto& col : route.second) { + refColors[route.first][col.first].insert( + refColors[route.first][col.first].end(), col.second.begin(), + col.second.end()); + } + } + } + + // update them in the routes, split routes if necessary + updateRouteColors(refColors); if (_cfg.buildTransitGraph) { - LOG(INFO) << "Building transit network graph..."; - buildTrGraph(>fsGraph, ng); + LOG(DEBUG) << "Building transit network graph..."; + + // merge gtfsgraph from threads + TrGraphEdgs gtfsGraph; + + for (auto& g : gtfsGraphs) { + for (auto& ePair : g) { + gtfsGraph[ePair.first].insert(gtfsGraph[ePair.first].begin(), + ePair.second.begin(), ePair.second.end()); + } + } + buildNetGraph(>fsGraph, outNg); + } + + stats.dijkstraIters = EDijkstra::ITERS; + + return stats; +} + +// _____________________________________________________________________________ +void ShapeBuilder::updateRouteColors(const RouteRefColors& refColors) { + for (auto& route : refColors) { + if (route.second.size() == 1) { + // only one color found for this route, great! + // update inplace... + route.first->setColor(route.second.begin()->first); + if (route.first->getColor() != NO_COLOR) + route.first->setTextColor(getTextColor(route.first->getColor())); + } else { + // are there fare rules using this route? + std::vector< + std::pair*, + ad::cppgtfs::gtfs::FareRule>> + rules; + + for (auto& f : _feed->getFares()) { + for (auto r : f.second->getFareRules()) { + if (r.getRoute() == route.first) { + rules.push_back({f.second, r}); + } + } + } + + // add new routes... + for (auto& c : route.second) { + // keep the original one intact + if (c.first == route.first->getColor()) continue; + + auto routeCp = *route.first; + + // find free id + std::string newId = route.first->getId() + "::1"; + size_t i = 1; + while (_feed->getRoutes().get(newId)) { + i++; + newId = route.first->getId() + "::" + std::to_string(i); + } + + routeCp.setId(newId); + routeCp.setColor(c.first); + routeCp.setTextColor(getTextColor(routeCp.getColor())); + + auto newRoute = _feed->getRoutes().add(routeCp); + + // update trips to use that route + for (auto& t : c.second) t->setRoute(newRoute); + + // add new fare rules + for (auto a : rules) { + auto rule = a.second; + rule.setRoute(newRoute); + a.first->addFareRule(rule); + } + } + } } } // _____________________________________________________________________________ void ShapeBuilder::setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, - const std::vector& distances) { + const std::vector& distances) { assert(distances.size() == t->getStopTimes().size()); // set distances size_t i = 0; @@ -291,91 +547,44 @@ void ShapeBuilder::setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, } std::lock_guard guard(_shpMutex); - t->setShape(_feed->getShapes().add(s)); + auto gtfsShp = _feed->getShapes().add(s); + t->setShape(gtfsShp); } // _____________________________________________________________________________ ad::cppgtfs::gtfs::Shape ShapeBuilder::getGtfsShape( - const Shape& shp, Trip* t, std::vector* hopDists) { + const EdgeListHops& hops, Trip* t, const RoutingAttrs& rAttrs, + std::vector* hopDists, uint32_t* bestColor) { ad::cppgtfs::gtfs::Shape ret(getFreeShapeId(t)); - assert(shp.hops.size() == t->getStopTimes().size() - 1); + assert(hops.size() == t->getStopTimes().size() - 1); + + std::map colors; + + const std::vector& gl = getGeom(hops, rAttrs, &colors); + const std::vector& measures = getMeasure(gl); size_t seq = 0; - double dist = -1; - double lastDist = -1; hopDists->push_back(0); - POINT last(0, 0); - for (const auto& hop : shp.hops) { - const trgraph::Node* l = hop.start; - if (hop.edges.size() == 0) { - POINT ll = webMercToLatLng( - hop.start->pl().getGeom()->getX(), hop.start->pl().getGeom()->getY()); - - if (dist > -0.5) - dist += webMercMeterDist(last, *hop.start->pl().getGeom()); - else - dist = 0; - - last = *hop.start->pl().getGeom(); - - if (dist - lastDist > 0.01) { - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } - - dist += webMercMeterDist(last, *hop.end->pl().getGeom()); - last = *hop.end->pl().getGeom(); - - if (dist - lastDist > 0.01) { - ll = webMercToLatLng( - hop.end->pl().getGeom()->getX(), hop.end->pl().getGeom()->getY()); - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } + for (size_t i = 0; i < gl.size(); i++) { + for (size_t j = 0; j < gl[i].size(); j++) { + ret.addPoint( + ShapePoint(gl[i][j].getY(), gl[i][j].getX(), measures[seq], seq)); + seq++; } + hopDists->push_back(measures[seq - 1]); + } - for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) { - const auto* e = *i; - if ((e->getFrom() == l) ^ e->pl().isRev()) { - for (size_t i = 0; i < e->pl().getGeom()->size(); i++) { - const POINT& cur = (*e->pl().getGeom())[i]; - if (dist > -0.5) - dist += webMercMeterDist(last, cur); - else - dist = 0; - last = cur; - if (dist - lastDist > 0.01) { - POINT ll = - webMercToLatLng(cur.getX(), cur.getY()); - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } - } - } else { - for (int64_t i = e->pl().getGeom()->size() - 1; i >= 0; i--) { - const POINT& cur = (*e->pl().getGeom())[i]; - if (dist > -0.5) - dist += webMercMeterDist(last, cur); - else - dist = 0; - last = cur; - if (dist - lastDist > 0.01) { - POINT ll = - webMercToLatLng(cur.getX(), cur.getY()); - ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); - seq++; - lastDist = dist; - } - } - } - l = e->getOtherNd(l); + // get most likely color + double best = 0; + *bestColor = NO_COLOR; + for (const auto& c : colors) { + double progr = c.second / measures.back(); + // TODO(patrick): make threshold configurable + if (progr > 0.9 && progr > best) { + best = progr; + *bestColor = c.first; } - - hopDists->push_back(lastDist); } return ret; @@ -402,21 +611,24 @@ const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) { if (i == _rAttrs.end()) { router::RoutingAttrs ret; + ret.classifier = _classifier; + const auto& lnormzer = _motCfg.osmBuildOpts.lineNormzer; + const auto& snormzer = _motCfg.osmBuildOpts.statNormzer; ret.shortName = lnormzer.norm(trip->getRoute()->getShortName()); + ret.lineFrom = + snormzer.norm(trip->getStopTimes().front().getStop()->getName()); + ret.lineTo = { + snormzer.norm(trip->getStopTimes().back().getStop()->getName())}; + // fallbacks for line name if (ret.shortName.empty()) ret.shortName = lnormzer.norm(trip->getShortname()); if (ret.shortName.empty()) ret.shortName = lnormzer.norm(trip->getRoute()->getLongName()); - ret.fromString = _motCfg.osmBuildOpts.statNormzer.norm( - trip->getStopTimes().begin()->getStop()->getName()); - ret.toString = _motCfg.osmBuildOpts.statNormzer.norm( - (--trip->getStopTimes().end())->getStop()->getName()); - return _rAttrs .insert(std::pair(trip, ret)) .first->second; @@ -433,7 +645,7 @@ const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) const { // _____________________________________________________________________________ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, const std::string& tid, bool dropShapes, - osm::BBoxIdx* box) { + osm::BBoxIdx* box, double maxSpeed) { for (const auto& t : feed->getTrips()) { if (!tid.empty() && t.getId() != tid) continue; if (tid.empty() && !t.getShape().empty() && !dropShapes) continue; @@ -441,7 +653,49 @@ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, if (mots.count(t.getRoute()->getType())) { DBox cur; - for (const auto& st : t.getStopTimes()) { + for (size_t i = 0; i < t.getStopTimes().size(); i++) { + // skip outliers + const auto& st = t.getStopTimes()[i]; + + int toTime = std::numeric_limits::max(); + double toD = 0; + int fromTime = std::numeric_limits::max(); + double fromD = 0; + + if (i > 0) { + const auto& stPrev = t.getStopTimes()[i - 1]; + toTime = st.getArrivalTime().seconds() - + stPrev.getDepartureTime().seconds(); + toD = util::geo::haversine( + st.getStop()->getLat(), st.getStop()->getLng(), + stPrev.getStop()->getLat(), stPrev.getStop()->getLng()); + } + + if (i < t.getStopTimes().size() - 1) { + const auto& stNext = t.getStopTimes()[i + 1]; + fromTime = stNext.getArrivalTime().seconds() - + st.getDepartureTime().seconds(); + fromD = util::geo::haversine( + st.getStop()->getLat(), st.getStop()->getLng(), + stNext.getStop()->getLat(), stNext.getStop()->getLng()); + } + + const double reqToTime = toD / maxSpeed; + const double reqFromTime = fromD / maxSpeed; + + const double BUFFER = 5 * 60; + + if (reqToTime > (BUFFER + toTime) * 3 * MAX_ROUTE_COST_DOUBLING_STEPS && + reqFromTime > + (BUFFER + fromTime) * 3 * MAX_ROUTE_COST_DOUBLING_STEPS) { + LOG(DEBUG) << "Skipping station " << st.getStop()->getId() << " (" + << st.getStop()->getName() << ") @ " + << st.getStop()->getLat() << ", " << st.getStop()->getLng() + << " for bounding box as the vehicle cannot realistically " + "reach and leave it in the scheduled time"; + continue; + } + cur = extendBox(DPoint(st.getStop()->getLng(), st.getStop()->getLat()), cur); } @@ -451,141 +705,224 @@ void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots, } // _____________________________________________________________________________ -NodeCandRoute ShapeBuilder::getNCR(Trip* trip) const { - router::NodeCandRoute ncr(trip->getStopTimes().size()); +std::vector ShapeBuilder::getTransTimes(Trip* trip) const { + std::vector ret; - size_t i = 0; + for (size_t i = 0; i < trip->getStopTimes().size() - 1; i++) { + auto cur = trip->getStopTimes()[i]; + auto next = trip->getStopTimes()[i + 1]; - for (const auto& st : trip->getStopTimes()) { - ncr[i] = getNodeCands(st.getStop()); - if (ncr[i].size() == 0) { - throw std::runtime_error("No node candidate found for station '" + - st.getStop()->getName() + "' on trip '" + - trip->getId() + "'"); - } - i++; - } - return ncr; -} + int depTime = cur.getDepartureTime().seconds(); + int arrTime = next.getArrivalTime().seconds(); -// _____________________________________________________________________________ -double ShapeBuilder::avgHopDist(Trip* trip) const { - size_t i = 0; - double sum = 0; + int diff = arrTime - depTime; + if (diff < 1) diff = 1; - const Stop* prev = 0; - - for (const auto& st : trip->getStopTimes()) { - if (!prev) { - prev = st.getStop(); - continue; - } - auto a = util::geo::latLngToWebMerc(prev->getLat(), - prev->getLng()); - auto b = util::geo::latLngToWebMerc( - st.getStop()->getLat(), st.getStop()->getLng()); - sum += util::geo::webMercMeterDist(a, b); - - prev = st.getStop(); - i++; - } - return sum / static_cast(i); -} - -// _____________________________________________________________________________ -Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) { - // building an index [start station, end station] -> [cluster] - - std::map> clusterIdx; - - Clusters ret; - for (auto& trip : f->getTrips()) { - if (!trip.getShape().empty() && !_cfg.dropShapes) continue; - if (trip.getStopTimes().size() < 2) continue; - if (!mots.count(trip.getRoute()->getType()) || - !_motCfg.mots.count(trip.getRoute()->getType())) - continue; - - bool found = false; - auto spair = StopPair(trip.getStopTimes().begin()->getStop(), - trip.getStopTimes().rbegin()->getStop()); - const auto& c = clusterIdx[spair]; - - for (size_t i = 0; i < c.size(); i++) { - if (routingEqual(ret[c[i]][0], &trip)) { - ret[c[i]].push_back(&trip); - found = true; - break; - } - } - if (!found) { - ret.push_back(Cluster{&trip}); - // explicit call to write render attrs to cache - getRAttrs(&trip); - clusterIdx[spair].push_back(ret.size() - 1); - } + ret.push_back(diff); + assert(ret.back() >= 0); } return ret; } // _____________________________________________________________________________ -bool ShapeBuilder::routingEqual(const Stop* a, const Stop* b) { - if (a == b) return true; // trivial +std::vector ShapeBuilder::getTransDists(Trip* trip) const { + std::vector ret; - auto namea = _motCfg.osmBuildOpts.statNormzer.norm(a->getName()); - auto nameb = _motCfg.osmBuildOpts.statNormzer.norm(b->getName()); - if (namea != nameb) return false; + for (size_t i = 0; i < trip->getStopTimes().size() - 1; i++) { + auto cur = trip->getStopTimes()[i]; + auto next = trip->getStopTimes()[i + 1]; - auto tracka = _motCfg.osmBuildOpts.trackNormzer.norm(a->getPlatformCode()); - auto trackb = _motCfg.osmBuildOpts.trackNormzer.norm(b->getPlatformCode()); - if (tracka != trackb) return false; + double dist = util::geo::haversine( + cur.getStop()->getLat(), cur.getStop()->getLng(), + next.getStop()->getLat(), next.getStop()->getLng()); - POINT ap = - util::geo::latLngToWebMerc(a->getLat(), a->getLng()); - POINT bp = - util::geo::latLngToWebMerc(b->getLat(), b->getLng()); + ret.push_back(dist); + } - double d = util::geo::webMercMeterDist(ap, bp); - - if (d > 1) return false; - - return true; + return ret; } // _____________________________________________________________________________ -bool ShapeBuilder::routingEqual(Trip* a, Trip* b) { - if (a->getStopTimes().size() != b->getStopTimes().size()) return false; - if (getRAttrs(a) != getRAttrs(b)) return false; +EdgeCandMap ShapeBuilder::getECM(const TripTrie* trie) const { + EdgeCandMap ecm(trie->getNds().size()); - auto stb = b->getStopTimes().begin(); - for (const auto& sta : a->getStopTimes()) { - if (!routingEqual(sta.getStop(), stb->getStop())) { - return false; + for (size_t nid = 1; nid < trie->getNds().size(); nid++) { + auto trNd = trie->getNds()[nid]; + auto parentTrNd = trie->getNds()[trNd.parent]; + + if (nid != 1 && !trNd.arr) continue; + + double avgT = 0; + + if (trNd.trips) avgT = trNd.accTime / trNd.trips; + + const auto& cands = getEdgCands(trNd.reprStop); + ecm[nid].reserve(cands.size()); + + for (auto& cand : cands) { + const auto& timeExpCands = timeExpand(cand, avgT); + assert(timeExpCands.size()); + + for (size_t depChildId : trNd.childs) { + if (nid == 1) break; + auto chldTrNd = trie->getNds()[depChildId]; + double avgChildT = 0; + if (chldTrNd.trips) avgChildT = chldTrNd.accTime / chldTrNd.trips; + + double timeDiff = avgChildT - avgT; + if (timeDiff < 0) timeDiff = 0; + + for (size_t candId = 0; candId < timeExpCands.size(); candId++) { + const auto& cand = timeExpCands[candId]; + ecm[depChildId].push_back(cand); + ecm[depChildId].back().time += timeDiff; + + ecm[depChildId].back().pen = timePen(cand.time, avgChildT); + + for (size_t sucCandId = 0; sucCandId < timeExpCands.size(); + sucCandId++) { + if (timeExpCands[sucCandId].time <= ecm[depChildId].back().time) { + ecm[depChildId].back().depPrede.push_back(sucCandId + + ecm[nid].size()); + } + } + assert(ecm[depChildId].back().depPrede.size()); + } + } + ecm[nid].insert(ecm[nid].end(), timeExpCands.begin(), timeExpCands.end()); } - stb++; + + assert(ecm[nid].size() != 0); } - return true; + return ecm; +} + +// _____________________________________________________________________________ +double ShapeBuilder::timePen(int candTime, int schedTime) const { + // standard deviation of normal distribution + double standarddev = 5 * 60; + + int diff = abs(candTime - schedTime); + + double cNorm = (diff) / standarddev; + return cNorm * cNorm; +} + +// _____________________________________________________________________________ +EdgeCandGroup ShapeBuilder::timeExpand(const EdgeCand& ec, int time) const { + EdgeCandGroup ret; + // TODO(patrick): heuristic for time expansion variance + // for (int i = -5; i < 6; i++) { + // for (int i = -10; i < 1; i++) { + for (int i = 0; i < 1; i++) { + EdgeCand ecNew = ec; + // in 30 sec steps + ecNew.time = time + i * 30; + ecNew.pen = ecNew.pen + timePen(ecNew.time, time); + ret.push_back(ecNew); + } + + return ret; +} + +// _____________________________________________________________________________ +TripForests ShapeBuilder::clusterTrips(Feed* f, MOTs mots) { + TripForests forest; + std::map> trips; + + // warm the stop name normalizer caches so a + // multithreaded access later on will never write to the underlying cache + for (auto& stop : f->getStops()) { + const auto& snormzer = _motCfg.osmBuildOpts.statNormzer; + auto normedName = snormzer.norm(stop.getName()); + } + + // cluster by routing attr for parallization later on + for (auto& trip : f->getTrips()) { + if (!_cfg.dropShapes && !trip.getShape().empty()) continue; + if (trip.getStopTimes().size() < 2) continue; + if (!mots.count(trip.getRoute()->getType()) || + !_motCfg.mots.count(trip.getRoute()->getType())) + continue; + + // important: we are building the routing attributes here, so a + // multithreaded access later on will never write to the underlying cache + const auto& rAttrs = getRAttrs(&trip); + + trips[rAttrs].push_back(&trip); + forest[rAttrs] = {}; + } + + size_t numThreads = std::thread::hardware_concurrency(); + std::vector thrds(numThreads); + std::vector> attrs(numThreads); + + size_t i = 0; + for (auto it : trips) { + attrs[i].push_back(it.first); + if (++i == numThreads) i = 0; + } + + i = 0; + for (auto& t : thrds) { + t = std::thread(&ShapeBuilder::clusterWorker, this, &attrs[i], &trips, + &forest); + i++; + } + + for (auto& thr : thrds) thr.join(); + + return forest; +} + +// _____________________________________________________________________________ +void ShapeBuilder::clusterWorker( + const std::vector* rAttrsVec, + const std::map>* trips, + TripForests* forest) { + for (const auto& rAttrs : *rAttrsVec) { + for (auto& trip : trips->at(rAttrs)) { + bool ins = false; + auto& subForest = forest->at(rAttrs); + for (auto& trie : subForest) { + if (trie.addTrip(trip, rAttrs, + _motCfg.routingOpts.transPenMethod == "timenorm", + _cfg.noTrie)) { + ins = true; + break; + } + } + + if (!ins) { + subForest.resize(subForest.size() + 1); + subForest.back().addTrip( + trip, rAttrs, _motCfg.routingOpts.transPenMethod == "timenorm", + false); + } + } + } } // _____________________________________________________________________________ const pfaedle::trgraph::Graph* ShapeBuilder::getGraph() const { return _g; } // _____________________________________________________________________________ -void ShapeBuilder::writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs, - const Cluster& cluster) const { - for (auto hop : shp.hops) { +void ShapeBuilder::writeTransitGraph( + const router::EdgeListHops& hops, TrGraphEdgs* edgs, + const std::vector& trips) const { + for (const auto& hop : hops) { for (const auto* e : hop.edges) { if (e->pl().isRev()) e = _g->getEdg(e->getTo(), e->getFrom()); - (*edgs)[e].insert(cluster.begin(), cluster.end()); + (*edgs)[e].insert((*edgs)[e].begin(), trips.begin(), trips.end()); } } } // _____________________________________________________________________________ -void ShapeBuilder::buildTrGraph(TrGraphEdgs* edgs, - pfaedle::netgraph::Graph* ng) const { +void ShapeBuilder::buildNetGraph(TrGraphEdgs* edgs, + pfaedle::netgraph::Graph* ng) const { std::unordered_map nodes; for (auto ep : *edgs) { @@ -607,3 +944,275 @@ void ShapeBuilder::buildTrGraph(TrGraphEdgs* edgs, pfaedle::netgraph::EdgePL(*e->pl().getGeom(), ep.second)); } } + +// _____________________________________________________________________________ +std::vector ShapeBuilder::getGeom( + const EdgeListHops& hops, const RoutingAttrs& rAttrs, + std::map* colors) const { + std::vector ret; + + for (size_t i = hops.size(); i > 0; i--) { + const auto& hop = hops[i - 1]; + if (!hop.start || !hop.end) { + // no hop was found, use the fallback geometry + + if (hop.start) { + auto l = getLine(hop.start); + if (hop.progrStart > 0) { + PolyLine pl(l); + const auto& seg = pl.getSegment(hop.progrStart, 1); + ret.push_back({seg.getLine().front(), hop.pointEnd}); + } else { + ret.push_back({*hop.start->getFrom()->pl().getGeom(), hop.pointEnd}); + } + } else if (hop.end) { + auto l = getLine(hop.end); + if (hop.progrEnd > 0) { + PolyLine pl(l); + const auto& seg = pl.getSegment(0, hop.progrEnd); + ret.push_back({hop.pointStart, seg.getLine().back()}); + } else { + ret.push_back({hop.pointStart, *hop.end->getFrom()->pl().getGeom()}); + } + } else { + ret.push_back({hop.pointEnd, hop.pointStart}); + } + } else { + const auto& l = getLine(hop, rAttrs, colors); + ret.push_back(l); + } + } + + return ret; +} + +// _____________________________________________________________________________ +LINE ShapeBuilder::getLine(const EdgeListHop& hop, const RoutingAttrs& rAttrs, + std::map* colors) const { + LINE l; + + const auto& curL = getLine(hop.start); + + if (hop.edges.size() == 0) { + // draw direct line between positions on edges + if (hop.progrStart > 0) { + PolyLine pl(curL); + const auto& seg = pl.getSegment(hop.progrStart, 1); + l.push_back(seg.front()); + } else { + l.push_back(curL.front()); + } + + if (hop.progrEnd > 0) { + PolyLine pl(getLine(hop.end)); + const auto& seg = pl.getSegment(0, hop.progrEnd); + l.push_back(seg.back()); + } else { + l.push_back(*hop.end->getFrom()->pl().getGeom()); + } + + return l; + } + + // special case: start and end are on the same edge! + if (hop.edges.size() == 1 && hop.start == hop.end) { + PolyLine pl(curL); + const auto& seg = pl.getSegment(hop.progrStart, hop.progrEnd); + l.insert(l.end(), seg.getLine().begin(), seg.getLine().end()); + + for (const auto& color : getColorMatch(hop.start, rAttrs)) { + (*colors)[color] += hop.start->pl().getLength(); + } + + return l; + } + + auto from = hop.start->getFrom(); + + if (hop.progrStart > 0) { + PolyLine pl(curL); + const auto& seg = pl.getSegment(hop.progrStart, 1); + l.insert(l.end(), seg.getLine().begin(), seg.getLine().end()); + + double l = hop.start->pl().getLength() * (1 - hop.progrStart); + for (const auto& color : getColorMatch(hop.start, rAttrs)) { + (*colors)[color] += l; + } + } else { + l.insert(l.end(), curL.begin(), curL.end()); + + double l = hop.start->pl().getLength(); + for (const auto& color : getColorMatch(hop.start, rAttrs)) { + (*colors)[color] += l; + } + } + + from = hop.start->getOtherNd(from); + + if (hop.edges.size() > 1) { + for (size_t j = hop.edges.size() - 2; j > 0; j--) { + const auto* e = hop.edges[j]; + const auto& curL = getLine(e); + l.insert(l.end(), curL.begin(), curL.end()); + from = e->getOtherNd(from); + + double l = e->pl().getLength(); + for (const auto& color : getColorMatch(e, rAttrs)) { + (*colors)[color] += l; + } + } + } + + if (hop.progrEnd > 0) { + PolyLine pl(getLine(hop.end)); + const auto& seg = pl.getSegment(0, hop.progrEnd); + l.insert(l.end(), seg.getLine().begin(), seg.getLine().end()); + + double l = hop.end->pl().getLength() * hop.progrEnd; + for (const auto& color : getColorMatch(hop.end, rAttrs)) { + (*colors)[color] += l; + } + } + + if (l.size() > 1) return util::geo::simplify(l, 0.5 / M_PER_DEG); + return l; +} + +// _____________________________________________________________________________ +LINE ShapeBuilder::getLine(const trgraph::Edge* e) const { + LINE l; + if (!e->pl().getGeom() || e->pl().getGeom()->size() == 0) + return {*e->getFrom()->pl().getGeom(), *e->getTo()->pl().getGeom()}; + if (e->pl().isRev()) { + l.insert(l.end(), e->pl().getGeom()->rbegin(), e->pl().getGeom()->rend()); + } else { + l.insert(l.end(), e->pl().getGeom()->begin(), e->pl().getGeom()->end()); + } + return l; +} + +// _____________________________________________________________________________ +std::vector ShapeBuilder::getMeasure( + const std::vector& lines) const { + assert(lines.size()); + assert(lines.front().size()); + std::vector ret; + POINT last = lines.front().front(); + + for (const auto& l : lines) { + for (size_t i = 0; i < l.size(); i++) { + if (ret.size() == 0) { + ret.push_back(0); + } else { + float v = ret.back() + util::geo::haversine(last, l[i]); + assert(v >= ret.back()); // required by GTFS standard! + ret.push_back(v); + } + last = l[i]; + } + } + + return ret; +} + +// _____________________________________________________________________________ +void ShapeBuilder::shapeWorker( + const std::vector* tries, std::atomic* at, + std::map* shpUse, + std::map>>* routeColors, + TrGraphEdgs* gtfsGraph) { + while (1) { + size_t j = (*at)++; + if (j >= tries->size()) return; + + int step = tries->size() < 10 ? tries->size() : 10; + + if (j % (tries->size() / step) == 0) { + LOG(INFO) << "@ " << (static_cast((j * 1.0) / tries->size() * 100)) + << "%"; + LOG(DEBUG) << "(@ trie forest " << j << "/" << tries->size() << ")"; + } + + const auto& forest = *((*tries)[j]); + + // hop cache per forest, thus per routing attributes + HopCache hopCacheLoc; + HopCache* hopCache = 0; + + if (!_cfg.noHopCache) hopCache = &hopCacheLoc; + + for (size_t i = 0; i < forest.size(); i++) { + const TripTrie* trie = &(forest[i]); + const auto& hops = shapeify(trie, hopCache); + + for (const auto& leaf : trie->getNdTrips()) { + std::vector distances; + const RoutingAttrs& rAttrs = trie->getNd(leaf.first).rAttrs; + + uint32_t color; + + const ad::cppgtfs::gtfs::Shape& shp = getGtfsShape( + hops.at(leaf.first), leaf.second[0], rAttrs, &distances, &color); + + if (_cfg.buildTransitGraph) { + writeTransitGraph(hops.at(leaf.first), gtfsGraph, leaf.second); + } + + for (auto t : leaf.second) { + if (_cfg.writeColors && color != NO_COLOR && + t->getRoute()->getColor() == NO_COLOR && + t->getRoute()->getTextColor() == NO_COLOR) { + (*routeColors)[t->getRoute()][color].push_back(t); + } else { + // else, use the original route color + (*routeColors)[t->getRoute()][t->getRoute()->getColor()].push_back( + t); + } + + if (!t->getShape().empty() && (*shpUse)[t->getShape()] > 0) { + (*shpUse)[t->getShape()]--; + if ((*shpUse)[t->getShape()] == 0) { + std::lock_guard guard(_shpMutex); + _feed->getShapes().remove(t->getShape()); + } + } + setShape(t, shp, distances); + } + } + } + } +} + +// _____________________________________________________________________________ +void ShapeBuilder::edgCandWorker(std::vector* stops, + GrpCache* cache) { + for (auto stop : *stops) { + (*cache)[stop] = getEdgCands(stop); + } +} + +// _____________________________________________________________________________ +std::set ShapeBuilder::getColorMatch( + const trgraph::Edge* e, const RoutingAttrs& rAttrs) const { + std::set ret; + for (const auto* l : e->pl().getLines()) { + auto simi = rAttrs.simi(l); + if (simi.nameSimilar && l->color != NO_COLOR) ret.insert(l->color); + } + + return ret; +} + +// _____________________________________________________________________________ +uint32_t ShapeBuilder::getTextColor(uint32_t c) const { + double r = (c & 0x00FF0000) >> 16; + double g = (c & 0x0000FF00) >> 8; + double b = (c & 0x000000FF); + + // gray value approx + double a = sqrt((r * r + g * g + b * b) / 3); + + // below a certain gray value, use white, else black + if (a < 140) return 0x00FFFFFF; + return 0; +} diff --git a/src/pfaedle/router/ShapeBuilder.h b/src/pfaedle/router/ShapeBuilder.h index 560d1b2..8087e84 100644 --- a/src/pfaedle/router/ShapeBuilder.h +++ b/src/pfaedle/router/ShapeBuilder.h @@ -9,39 +9,41 @@ #include #include #include +#include #include #include #include "ad/cppgtfs/gtfs/Feed.h" #include "pfaedle/Def.h" #include "pfaedle/config/MotConfig.h" #include "pfaedle/config/PfaedleConfig.h" -#include "pfaedle/eval/Collector.h" #include "pfaedle/gtfs/Feed.h" #include "pfaedle/netgraph/Graph.h" #include "pfaedle/osm/Restrictor.h" #include "pfaedle/router/Misc.h" #include "pfaedle/router/Router.h" +#include "pfaedle/router/Stats.h" +#include "pfaedle/router/TripTrie.h" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" #include "pfaedle/trgraph/Graph.h" #include "util/geo/Geo.h" namespace pfaedle { namespace router { -using ad::cppgtfs::gtfs::Stop; -using pfaedle::gtfs::Trip; -using pfaedle::gtfs::Feed; - -struct Shape { - router::EdgeListHops hops; - double avgHopDist; -}; - -typedef std::vector Cluster; -typedef std::vector Clusters; -typedef std::pair StopPair; -typedef std::unordered_map TripRAttrs; -typedef std::unordered_map> +typedef std::vector TripForest; +typedef std::map TripForests; +typedef std::pair + StopPair; +typedef std::unordered_map + TripRAttrs; +typedef std::unordered_map> TrGraphEdgs; +typedef std::map>> + RouteRefColors; +typedef std::unordered_map + GrpCache; /* * Layer class for the router. Provides an interface for direct usage with @@ -49,76 +51,116 @@ typedef std::unordered_map> */ class ShapeBuilder { public: - ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots, - const config::MotConfig& motCfg, eval::Collector* ecoll, - trgraph::Graph* g, router::FeedStops* stops, - osm::Restrictor* restr, const config::Config& cfg); + ShapeBuilder( + pfaedle::gtfs::Feed* feed, MOTs mots, const config::MotConfig& motCfg, + trgraph::Graph* g, router::FeedStops* stops, osm::Restrictor* restr, + const pfaedle::statsimiclassifier::StatsimiClassifier* classifier, + router::Router* router, const config::Config& cfg); - void shape(pfaedle::netgraph::Graph* ng); + Stats shapeify(pfaedle::netgraph::Graph* outNg); router::FeedStops* getFeedStops(); - const NodeCandGroup& getNodeCands(const Stop* s) const; + // shape single trip + std::pair, Stats> shapeL(pfaedle::gtfs::Trip* trip); - LINE shapeL(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs); - LINE shapeL(Trip* trip); - - pfaedle::router::Shape shape(Trip* trip) const; - pfaedle::router::Shape shape(Trip* trip); + std::map shapeify(const TripTrie* trie, + HopCache* hopCache) const; + EdgeListHops shapeify(pfaedle::gtfs::Trip* trip); const trgraph::Graph* getGraph() const; - static void getGtfsBox(const Feed* feed, const MOTs& mots, + static void getGtfsBox(const pfaedle::gtfs::Feed* feed, const MOTs& mots, const std::string& tid, bool dropShapes, - osm::BBoxIdx* box); + osm::BBoxIdx* box, double maxSpeed); private: - Feed* _feed; - ad::cppgtfs::gtfs::Feed* _evalFeed; + pfaedle::gtfs::Feed* _feed; MOTs _mots; config::MotConfig _motCfg; - eval::Collector* _ecoll; config::Config _cfg; trgraph::Graph* _g; - router::Router _crouter; - router::FeedStops* _stops; - NodeCandGroup _emptyNCG; + EdgeCandGroup _emptyNCG; - size_t _curShpCnt, _numThreads; + size_t _curShpCnt; std::mutex _shpMutex; TripRAttrs _rAttrs; osm::Restrictor* _restr; + const pfaedle::statsimiclassifier::StatsimiClassifier* _classifier; + GrpCache _grpCache; - void buildGraph(router::FeedStops* fStops); + router::Router* _router; - Clusters clusterTrips(Feed* f, MOTs mots); - void writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs, - const Cluster& cluster) const; - void buildTrGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const; + TripForests clusterTrips(pfaedle::gtfs::Feed* f, MOTs mots); + void buildNetGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const; - std::string getFreeShapeId(Trip* t); + std::string getFreeShapeId(pfaedle::gtfs::Trip* t); + ad::cppgtfs::gtfs::Shape getGtfsShape(const EdgeListHops& shp, + pfaedle::gtfs::Trip* t, + const RoutingAttrs& rAttrs, + std::vector* hopDists, + uint32_t* bestColor); - ad::cppgtfs::gtfs::Shape getGtfsShape(const Shape& shp, Trip* t, - std::vector* hopDists); + void setShape(pfaedle::gtfs::Trip* t, const ad::cppgtfs::gtfs::Shape& s, + const std::vector& dists); - void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s, - const std::vector& dists); + EdgeCandGroup getEdgCands(const ad::cppgtfs::gtfs::Stop* s) const; - router::NodeCandRoute getNCR(Trip* trip) const; - double avgHopDist(Trip* trip) const; - const router::RoutingAttrs& getRAttrs(const Trip* trip) const; - const router::RoutingAttrs& getRAttrs(const Trip* trip); - bool routingEqual(Trip* a, Trip* b); - bool routingEqual(const Stop* a, const Stop* b); - router::EdgeListHops route(const router::NodeCandRoute& ncr, - const router::RoutingAttrs& rAttrs) const; + router::EdgeCandMap getECM(const TripTrie* trie) const; + std::vector getTransTimes(pfaedle::gtfs::Trip* trip) const; + std::vector getTransDists(pfaedle::gtfs::Trip* trip) const; + const router::RoutingAttrs& getRAttrs(const pfaedle::gtfs::Trip* trip) const; + const router::RoutingAttrs& getRAttrs(const pfaedle::gtfs::Trip* trip); + std::map route(const TripTrie* trie, + const EdgeCandMap& ecm, + HopCache* hopCache) const; + void buildCandCache(const TripForests& clusters); + void buildIndex(); + + std::vector getGeom(const EdgeListHops& shp, const RoutingAttrs& rAttrs, + std::map* colors) const; + double timePen(int candTime, int schedTime) const; + + LINE getLine(const EdgeListHop& hop, const RoutingAttrs&, + std::map* colMap) const; + LINE getLine(const trgraph::Edge* edg) const; + std::vector getMeasure(const std::vector& lines) const; + + trgraph::Edge* deg2reachable(trgraph::Edge* e, + std::set edgs) const; + + EdgeCandGroup timeExpand(const EdgeCand& ec, int time) const; + + std::set getColorMatch(const trgraph::Edge* e, + const RoutingAttrs& rAttrs) const; + + void updateRouteColors(const RouteRefColors& c); + + uint32_t getTextColor(uint32_t c) const; + + void writeTransitGraph(const router::EdgeListHops& shp, TrGraphEdgs* edgs, + const std::vector& trips) const; + + void shapeWorker( + const std::vector* tries, std::atomic* at, + std::map* shpUsage, + std::map>>*, + TrGraphEdgs* gtfsGraph); + + void edgCandWorker(std::vector* stops, GrpCache* cache); + void clusterWorker(const std::vector* rAttrs, + const std::map>* trips, + TripForests* forest); + + pfaedle::trgraph::EdgeGrid _eGrid; + pfaedle::trgraph::NodeGrid _nGrid; }; + } // namespace router } // namespace pfaedle diff --git a/src/pfaedle/router/Stats.h b/src/pfaedle/router/Stats.h new file mode 100644 index 0000000..c01f0e2 --- /dev/null +++ b/src/pfaedle/router/Stats.h @@ -0,0 +1,33 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_STATS_H_ +#define PFAEDLE_ROUTER_STATS_H_ + +#include +#include +#include +#include "util/String.h" + +namespace pfaedle { +namespace router { + +struct Stats { + Stats() + : totNumTrips(0), + numTries(0), + numTrieLeafs(0), + solveTime(0), + dijkstraIters(0) {} + size_t totNumTrips; + size_t numTries; + size_t numTrieLeafs; + double solveTime; + size_t dijkstraIters; +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_STATS_H_ diff --git a/src/pfaedle/router/TripTrie.cpp b/src/pfaedle/router/TripTrie.cpp new file mode 100644 index 0000000..470ee09 --- /dev/null +++ b/src/pfaedle/router/TripTrie.cpp @@ -0,0 +1,219 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include "TripTrie.h" +#include "ad/cppgtfs/gtfs/Feed.h" +#include "pfaedle/gtfs/Feed.h" +#include "pfaedle/gtfs/StopTime.h" +#include "pfaedle/router/TripTrie.h" + +using pfaedle::gtfs::Trip; +using pfaedle::router::TripTrie; + +// _____________________________________________________________________________ +bool TripTrie::addTrip(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, + bool timeEx, bool degen) { + if (!degen) return add(trip, rAttrs, timeEx); + + // check if trip is already fully and uniquely contained, if not, fail + size_t existing = get(trip, timeEx); + if (existing && _nds[existing].childs.size() == 0) { + _tripNds[trip] = existing; + _ndTrips[existing].push_back(trip); + return true; + } else { + return false; + } +} + +// _____________________________________________________________________________ +bool TripTrie::add(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, + bool timeEx) { + if (trip->getStopTimes().size() == 0) return false; + + int startSecs = trip->getStopTimes().front().getDepartureTime().seconds(); + + size_t curNdId = 0; + for (size_t stId = 0; stId < trip->getStopTimes().size(); stId++) { + const auto st = trip->getStopTimes()[stId]; + + std::string name = st.getStop()->getName(); + std::string platform = st.getStop()->getPlatformCode(); + POINT pos = util::geo::latLngToWebMerc(st.getStop()->getLat(), + st.getStop()->getLng()); + + if (stId > 0) { + int arrTime = st.getArrivalTime().seconds() - startSecs; + + size_t arrChild = + getMatchChild(curNdId, name, platform, pos, arrTime, timeEx); + + if (arrChild) { + curNdId = arrChild; + + _nds[arrChild].accTime += arrTime; + _nds[arrChild].trips += 1; + + _nds[arrChild].rAttrs.merge(rAttrs); + } else { + curNdId = insert(st.getStop(), rAttrs, pos, arrTime, true, curNdId); + } + } + + if (stId < trip->getStopTimes().size() - 1) { + int depTime = st.getDepartureTime().seconds() - startSecs; + + size_t depChild = + getMatchChild(curNdId, name, platform, pos, depTime, timeEx); + + if (depChild) { + curNdId = depChild; + + _nds[depChild].accTime += depTime; + _nds[depChild].trips += 1; + + _nds[depChild].rAttrs.merge(rAttrs); + } else { + if (stId == 0 && _tripNds.size() > 0) return false; + curNdId = insert(st.getStop(), rAttrs, pos, depTime, false, curNdId); + } + } + } + + // curNdId is now the last matching node, insert the trip here + _tripNds[trip] = curNdId; + _ndTrips[curNdId].push_back(trip); + + return true; +} + +// _____________________________________________________________________________ +size_t TripTrie::get(pfaedle::gtfs::Trip* trip, bool timeEx) { + if (trip->getStopTimes().size() == 0) return false; + + int startSecs = trip->getStopTimes().front().getDepartureTime().seconds(); + + size_t curNdId = 0; + for (size_t stId = 0; stId < trip->getStopTimes().size(); stId++) { + const auto st = trip->getStopTimes()[stId]; + + std::string name = st.getStop()->getName(); + std::string platform = st.getStop()->getPlatformCode(); + POINT pos = util::geo::latLngToWebMerc(st.getStop()->getLat(), + st.getStop()->getLng()); + + if (stId > 0) { + int arrTime = st.getArrivalTime().seconds() - startSecs; + + size_t arrChild = + getMatchChild(curNdId, name, platform, pos, arrTime, timeEx); + + if (arrChild) { + curNdId = arrChild; + } else { + return 0; + } + } + + if (stId < trip->getStopTimes().size() - 1) { + int depTime = st.getDepartureTime().seconds() - startSecs; + + size_t depChild = + getMatchChild(curNdId, name, platform, pos, depTime, timeEx); + + if (depChild) { + curNdId = depChild; + } else { + return 0; + } + } + } + + return curNdId; +} + +// _____________________________________________________________________________ +size_t TripTrie::insert(const ad::cppgtfs::gtfs::Stop* stop, + const RoutingAttrs& rAttrs, const POINT& pos, int time, + bool arr, size_t parent) { + _nds.emplace_back(TripTrieNd{stop, + stop->getName(), + stop->getPlatformCode(), + pos, + stop->getLat(), + stop->getLng(), + time, + arr, + time, + 1, + parent, + {}, + rAttrs}); + _nds[parent].childs.push_back(_nds.size() - 1); + return _nds.size() - 1; +} + +// _____________________________________________________________________________ +const std::vector& TripTrie::getNds() const { + return _nds; +} + +// _____________________________________________________________________________ +size_t TripTrie::getMatchChild(size_t parentNid, const std::string& stopName, + const std::string& platform, POINT pos, int time, + bool timeEx) const { + for (size_t child : _nds[parentNid].childs) { + // TODO(patrick): use similarity classification here? + if (_nds[child].stopName == stopName && _nds[child].platform == platform && + util::geo::dist(_nds[child].pos, pos) < 1 && + (!timeEx || _nds[child].time == time)) { + return child; + } + } + + return 0; +} + +// _____________________________________________________________________________ +void TripTrie::toDot(std::ostream& os, const std::string& rootName, + size_t gid) const { + os << "digraph triptrie" << gid << " {"; + + for (size_t nid = 0; nid < _nds.size(); nid++) { + std::string color = "white"; + if (_ndTrips.count(nid)) color = "red"; + if (nid == 0) { + os << "\"" << gid << ":0\" [label=\"" << rootName << "\"];\n"; + } else { + os << "\"" << gid << ":" << nid + << "\" [shape=\"box\" style=\"filled\" fillcolor=\"" << color + << "\" label=\"#" << nid << ", " << _nds[nid].stopName << "@" + << util::geo::getWKT(_nds[nid].pos) << " t=" << _nds[nid].time + << "\"];\n"; + } + } + + for (size_t nid = 0; nid < _nds.size(); nid++) { + for (size_t child : _nds[nid].childs) { + os << "\"" << gid << ":" << nid << "\" -> \"" << gid << ":" << child + << "\";\n"; + } + } + + os << "}"; +} + +// _____________________________________________________________________________ +const std::map>& +TripTrie::getNdTrips() const { + return _ndTrips; +} + +// _____________________________________________________________________________ +const pfaedle::router::TripTrieNd& TripTrie::getNd(size_t nid) const { + return _nds[nid]; +} diff --git a/src/pfaedle/router/TripTrie.h b/src/pfaedle/router/TripTrie.h new file mode 100644 index 0000000..e03217f --- /dev/null +++ b/src/pfaedle/router/TripTrie.h @@ -0,0 +1,65 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_TRIPTRIE_H_ +#define PFAEDLE_ROUTER_TRIPTRIE_H_ + +#include +#include +#include +#include "ad/cppgtfs/gtfs/Feed.h" +#include "pfaedle/gtfs/Feed.h" +#include "pfaedle/gtfs/StopTime.h" +#include "pfaedle/router/RoutingAttrs.h" + +namespace pfaedle { +namespace router { + +struct TripTrieNd { + const ad::cppgtfs::gtfs::Stop* reprStop; + std::string stopName; // the stop name at this node + std::string platform; // the platform of node + POINT pos; // the position of this node + double lat, lng; + int time; + bool arr; + int accTime; + size_t trips; + size_t parent; + std::vector childs; + RoutingAttrs rAttrs; +}; + +class TripTrie { + public: + // init node 0, this is the first decision node + TripTrie() : _nds(1) {} + bool addTrip(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, + bool timeEx, bool degen); + + const std::vector& getNds() const; + const TripTrieNd& getNd(size_t nid) const; + + void toDot(std::ostream& os, const std::string& rootName, size_t gid) const; + const std::map>& getNdTrips() const; + + private: + std::vector _nds; + std::map _tripNds; + std::map> _ndTrips; + + bool add(pfaedle::gtfs::Trip* trip, const RoutingAttrs& rAttrs, bool timeEx); + size_t get(pfaedle::gtfs::Trip* trip, bool timeEx); + + size_t getMatchChild(size_t parentNid, const std::string& stopName, + const std::string& platform, POINT pos, int time, + bool timeEx) const; + size_t insert(const ad::cppgtfs::gtfs::Stop* stop, const RoutingAttrs& rAttrs, + const POINT& pos, int time, bool arr, size_t parent); +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_TRIPTRIE_H_ diff --git a/src/pfaedle/router/Weights.cpp b/src/pfaedle/router/Weights.cpp new file mode 100644 index 0000000..70a3aef --- /dev/null +++ b/src/pfaedle/router/Weights.cpp @@ -0,0 +1,261 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include "pfaedle/router/Weights.h" + +using pfaedle::router::DistDiffTransWeight; +using pfaedle::router::ExpoTransWeight; +using pfaedle::router::LineSimilarity; +using pfaedle::router::NormDistrTransWeight; +using util::geo::haversine; + +// _____________________________________________________________________________ +ExpoTransWeight::DistHeur::DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) + : _rOpts(rOpts), _maxV(maxV), _maxCentD(0), _lastE(0) { + size_t c = 0; + double x = 0, y = 0; + + for (const auto to : tos) { + x += to->getFrom()->pl().getGeom()->getX(); + y += to->getFrom()->pl().getGeom()->getY(); + c++; + } + + x /= c; + y /= c; + + _center = POINT{x, y}; + + for (const auto to : tos) { + const double cur = haversine(*to->getFrom()->pl().getGeom(), _center); + if (cur > _maxCentD) _maxCentD = cur; + } + + _maxCentD /= _maxV; +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::DistHeur::operator()( + const trgraph::Edge* a, const std::set& b) const { + UNUSED(b); + + // avoid repeated calculation for the same edge over and over again + if (a == _lastE) return _lastC; + + _lastE = a; + + const double d = haversine(*a->getFrom()->pl().getGeom(), _center); + const double heur = fmax(0, (d / _maxV - _maxCentD) * 10); + + // avoid overflow + if (heur > std::numeric_limits::max()) { + _lastC = std::numeric_limits::max(); + ; + return _lastC; + } + + _lastC = heur; + return heur; +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::CostFunc::operator()(const trgraph::Edge* from, + const trgraph::Node* n, + const trgraph::Edge* to) const { + if (!from) return 0; + + uint32_t c = from->pl().getCost(); + + if (c == std::numeric_limits::max()) return c; + + if (from == _lastFrom) { + // the transit line simi calculation is independent of the "to" edge, so if + // the last "from" edge was the same, skip it! + c = _lastC; + } else if (!_noLineSimiPen) { + const auto& simi = transitLineSimi(from); + + if (!simi.nameSimilar) { + if (_rOpts.lineUnmatchedPunishFact < 1) { + c = std::ceil(static_cast(c) * _rOpts.lineUnmatchedPunishFact); + } else if (_rOpts.lineUnmatchedPunishFact > 1) { + double a = + std::round(static_cast(c) * _rOpts.lineUnmatchedPunishFact); + if (a > std::numeric_limits::max()) + return std::numeric_limits::max(); + c = a; + } + } + + if (!simi.fromSimilar) { + if (_rOpts.lineNameFromUnmatchedPunishFact < 1) { + c = std::ceil(static_cast(c) * + _rOpts.lineNameFromUnmatchedPunishFact); + } else if (_rOpts.lineNameFromUnmatchedPunishFact > 1) { + double a = std::round(static_cast(c) * + _rOpts.lineNameFromUnmatchedPunishFact); + if (a > std::numeric_limits::max()) + return std::numeric_limits::max(); + c = a; + } + } + + if (!simi.toSimilar) { + if (_rOpts.lineNameToUnmatchedPunishFact < 1) { + c = std::ceil(static_cast(c) * + _rOpts.lineNameToUnmatchedPunishFact); + } else if (_rOpts.lineNameToUnmatchedPunishFact > 1) { + double a = std::round(static_cast(c) * + _rOpts.lineNameToUnmatchedPunishFact); + if (a > std::numeric_limits::max()) + return std::numeric_limits::max(); + c = a; + } + } + + _lastC = c; + _lastFrom = from; + } + + uint32_t overflowCheck = c; + + if (n && !n->pl().isTurnCycle()) { + if (_rOpts.fullTurnPunishFac != 0 && from->getFrom() == to->getTo() && + from->getTo() == to->getFrom()) { + // trivial full turn + c += _rOpts.fullTurnPunishFac; + + if (c <= overflowCheck) return std::numeric_limits::max(); + overflowCheck = c; + } else if (_rOpts.fullTurnPunishFac != 0 && n->getDeg() > 2) { + // otherwise, only intersection angles will be punished + + double ang = util::geo::innerProd( + *n->pl().getGeom(), from->pl().backHop(), to->pl().frontHop()); + + if (ang < _rOpts.fullTurnAngle) { + c += _rOpts.fullTurnPunishFac; + if (c <= overflowCheck) return std::numeric_limits::max(); + overflowCheck = c; + } + } + + // turn restriction cost + if (_rOpts.turnRestrCost > 0 && from->pl().isRestricted() && + !_res.may(from, to, n)) { + c += _rOpts.turnRestrCost; + if (c <= overflowCheck) return std::numeric_limits::max(); + } + } + + return c; +} + +// _____________________________________________________________________________ +LineSimilarity ExpoTransWeight::CostFunc::transitLineSimi( + const trgraph::Edge* e) const { + if (_rAttrs.shortName.empty() && _rAttrs.lineFrom.empty() && + _rAttrs.lineTo.empty()) + return {true, true, true}; + + LineSimilarity best = {false, false, false}; + for (const auto* l : e->pl().getLines()) { + auto simi = _rAttrs.simi(l); + if (simi.nameSimilar && simi.toSimilar && simi.fromSimilar) return simi; + if (best < simi) best = simi; + } + + return best; +} + +// _____________________________________________________________________________ +double ExpoTransWeight::weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts) { + UNUSED(t0); + UNUSED(d); + UNUSED(d0); + return rOpts.transitionPen * static_cast(c) / 10.0; +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::invWeight(double c, const RoutingOpts& rOpts) { + return std::round((c / rOpts.transitionPen) * 10); +} + +// _____________________________________________________________________________ +uint32_t ExpoTransWeight::maxCost(double tTime, const RoutingOpts& rOpts) { + // abort after 3 times the scheduled time, but assume a min time of + // 1 minute! + return std::ceil(fmax(tTime, 60) * 3 * rOpts.lineUnmatchedPunishFact * + rOpts.lineNameToUnmatchedPunishFact * + rOpts.lineNameFromUnmatchedPunishFact * 10); +} + +// _____________________________________________________________________________ + +// _____________________________________________________________________________ +double NormDistrTransWeight::weight(uint32_t cs, double d, double t0, double d0, + const RoutingOpts& rOpts) { + UNUSED(d); + UNUSED(d0); + UNUSED(rOpts); + + double t = static_cast(cs) / 10.0; + + // standard deviation of normal distribution + double standarddev = 1; + + // no backwards time travel! + if (t0 < 0) return std::numeric_limits::infinity(); + + // always assume it takes at least 10 seconds to travel + t0 = fmax(10, t0); + + double cNorm = (t / t0 - 1) / standarddev; + double normWeight = cNorm * cNorm; + + double expWeight = ExpoTransWeight::weight(cs, d, t0, d0, rOpts); + + return normWeight + expWeight; +} + +// _____________________________________________________________________________ +uint32_t NormDistrTransWeight::invWeight(double c, const RoutingOpts& rOpts) { + UNUSED(rOpts); + UNUSED(c); + + throw(std::runtime_error("Cannot apply inv weight to DistDiffTransWeight")); +} + +// _____________________________________________________________________________ + +// _____________________________________________________________________________ +double DistDiffTransWeight::weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts) { + UNUSED(t0); + UNUSED(c); + // double mean = 250; // expectation value of 250 meters for buses + // double lambda = 1.0 / mean; + + double w = fabs(d - d0); + + return rOpts.transitionPen * w; +} + +// _____________________________________________________________________________ +uint32_t DistDiffTransWeight::invWeight(double c, const RoutingOpts& rOpts) { + UNUSED(rOpts); + UNUSED(c); + + throw(std::runtime_error("Cannot apply inv weight to DistDiffTransWeight")); +} + +// _____________________________________________________________________________ +uint32_t DistDiffTransWeight::maxCost(double tTime, const RoutingOpts& rOpts) { + UNUSED(tTime); + UNUSED(rOpts); + return std::numeric_limits::max(); +} diff --git a/src/pfaedle/router/Weights.h b/src/pfaedle/router/Weights.h new file mode 100644 index 0000000..646f93b --- /dev/null +++ b/src/pfaedle/router/Weights.h @@ -0,0 +1,161 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_ROUTER_WEIGHTS_H_ +#define PFAEDLE_ROUTER_WEIGHTS_H_ + +#include +#include "pfaedle/osm/Restrictor.h" +#include "pfaedle/router/Misc.h" +#include "pfaedle/router/RoutingAttrs.h" +#include "pfaedle/trgraph/Graph.h" +#include "util/graph/EDijkstra.h" + +namespace pfaedle { +namespace router { + +typedef util::graph::EDijkstra::CostFunc + RCostFunc; +typedef util::graph::EDijkstra::HeurFunc + RHeurFunc; + +class ExpoTransWeight { + public: + struct CostFunc : public RCostFunc { + CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, + const osm::Restrictor& res, uint32_t max) + : _rAttrs(rAttrs), + _rOpts(rOpts), + _res(res), + _inf(max), + _noLineSimiPen(false), + _lastFrom(0) { + if (_rAttrs.lineFrom.empty() && _rAttrs.lineTo.empty() && + _rAttrs.shortName.empty()) { + _noLineSimiPen = true; + } + if (_rOpts.lineUnmatchedPunishFact == 1) { + _noLineSimiPen = true; + } + } + + const RoutingAttrs& _rAttrs; + const RoutingOpts& _rOpts; + const osm::Restrictor& _res; + uint32_t _inf; + bool _noLineSimiPen; + mutable const trgraph::Edge* _lastFrom; + mutable uint32_t _lastC; + + uint32_t operator()(const trgraph::Edge* from, const trgraph::Node* n, + const trgraph::Edge* to) const; + uint32_t inf() const { return _inf; } + + LineSimilarity transitLineSimi(const trgraph::Edge* e) const; + }; + + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos); + + const RoutingOpts& _rOpts; + double _maxV; + POINT _center; + double _maxCentD; + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const; + mutable const trgraph::Edge* _lastE; + mutable uint32_t _lastC; + }; + + static uint32_t maxCost(double tTime, const RoutingOpts& rOpts); + static double weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts); + static uint32_t invWeight(double cost, const RoutingOpts& rOpts); + static const bool ALLOWS_FAST_ROUTE = true; + static const bool NEED_DIST = false; +}; + +class ExpoTransWeightNoHeur : public ExpoTransWeight { + public: + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) { + UNUSED(maxV); + UNUSED(rOpts); + UNUSED(tos); + } + + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const { + UNUSED(a); + UNUSED(b); + return 0; + } + }; +}; + +class NormDistrTransWeight : public ExpoTransWeight { + public: + static double weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts); + static uint32_t invWeight(double cost, const RoutingOpts& rOpts); + static const bool ALLOWS_FAST_ROUTE = false; + static const bool NEED_DIST = false; +}; + +class NormDistrTransWeightNoHeur : public NormDistrTransWeight { + public: + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) { + UNUSED(maxV); + UNUSED(rOpts); + UNUSED(tos); + } + + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const { + UNUSED(a); + UNUSED(b); + return 0; + } + }; +}; + +class DistDiffTransWeight : public ExpoTransWeight { + public: + static uint32_t maxCost(double tTime, const RoutingOpts& rOpts); + static double weight(uint32_t c, double d, double t0, double d0, + const RoutingOpts& rOpts); + static uint32_t invWeight(double cost, const RoutingOpts& rOpts); + static const bool ALLOWS_FAST_ROUTE = false; + static const bool NEED_DIST = true; +}; + +class DistDiffTransWeightNoHeur : public DistDiffTransWeight { + public: + struct DistHeur : RHeurFunc { + DistHeur(double maxV, const RoutingOpts& rOpts, + const std::set& tos) { + UNUSED(maxV); + UNUSED(rOpts); + UNUSED(tos); + } + + uint32_t operator()(const trgraph::Edge* a, + const std::set& b) const { + UNUSED(a); + UNUSED(b); + return 0; + } + }; +}; + +} // namespace router +} // namespace pfaedle + +#endif // PFAEDLE_ROUTER_WEIGHTS_H_ diff --git a/src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp b/src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp new file mode 100644 index 0000000..f354311 --- /dev/null +++ b/src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp @@ -0,0 +1,30 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include "pfaedle/Def.h" +#include "pfaedle/statsimi-classifier/StatsimiClassifier.h" +#include "util/geo/Geo.h" + +using pfaedle::statsimiclassifier::JaccardClassifier; + +// _____________________________________________________________________________ +bool JaccardClassifier::similar(const std::string& nameA, const POINT& posA, + const std::string& nameB, + const POINT& posB) const { + UNUSED(posA); + UNUSED(posB); + return similar(nameA, nameB); +} + +// _____________________________________________________________________________ +bool JaccardClassifier::similar(const std::string& nameA, + const std::string& nameB) const { + // hard similarity + if (nameA == nameB) return true; + + return util::jaccardSimi(nameA, nameB) > 0.45; // 0.45 from paper +} diff --git a/src/pfaedle/statsimi-classifier/StatsimiClassifier.h b/src/pfaedle/statsimi-classifier/StatsimiClassifier.h new file mode 100644 index 0000000..dac508b --- /dev/null +++ b/src/pfaedle/statsimi-classifier/StatsimiClassifier.h @@ -0,0 +1,35 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_ +#define PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_ + +#include +#include "pfaedle/Def.h" +#include "util/geo/Geo.h" + +namespace pfaedle { +namespace statsimiclassifier { + +class StatsimiClassifier { + public: + virtual bool similar(const std::string& nameA, const POINT& posA, + const std::string& nameB, const POINT& posB) const = 0; + + virtual bool similar(const std::string& nameA, + const std::string& nameB) const = 0; +}; + +class JaccardClassifier : public StatsimiClassifier { + public: + virtual bool similar(const std::string& nameA, const POINT& posA, + const std::string& nameB, const POINT& posB) const; + virtual bool similar(const std::string& nameA, + const std::string& nameB) const; +}; + +} // namespace statsimiclassifier +} // namespace pfaedle + +#endif // PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_ diff --git a/src/pfaedle/tests/CMakeLists.txt b/src/pfaedle/tests/CMakeLists.txt new file mode 100644 index 0000000..deb3632 --- /dev/null +++ b/src/pfaedle/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(pfaedleTest TestMain.cpp) +target_link_libraries(pfaedleTest pfaedle_dep util) diff --git a/src/pfaedle/tests/TestMain.cpp b/src/pfaedle/tests/TestMain.cpp new file mode 100644 index 0000000..4187a9b --- /dev/null +++ b/src/pfaedle/tests/TestMain.cpp @@ -0,0 +1,329 @@ +// Copyright 2020 +// Author: Patrick Brosi + +#include "pfaedle/osm/Restrictor.h" + +#define private public +#include "pfaedle/router/Router.h" +#undef private +#define private private + +using pfaedle::osm::Restrictor; +using pfaedle::router::CostMatrix; +using pfaedle::router::EdgeCandGroup; +using pfaedle::router::ExpoTransWeight; +using pfaedle::router::LayerCostsDAG; +using pfaedle::router::RouterImpl; +using pfaedle::router::RoutingAttrs; +using pfaedle::router::RoutingOpts; +using util::approx; + +// _____________________________________________________________________________ +uint32_t cmGet(const CostMatrix& m, size_t i, size_t j) { + for (const auto& e : m) { + if (e.first.first == i && e.first.second == j) return e.second; + } + + return -1; +} + +// _____________________________________________________________________________ +int main(int argc, char** argv) { + UNUSED(argc); + UNUSED(argv); + RouterImpl router; + + RoutingAttrs rAttrs; + RoutingOpts rOpts; + Restrictor restr; + LayerCostsDAG initCosts; + + // to make sure we always underestimate the cost in the heuristic for testing + pfaedle::trgraph::NodePL::comps.emplace_back( + pfaedle::trgraph::Component{9999999}); + + // build transit graph + pfaedle::trgraph::Graph g; + auto a = g.addNd(POINT{0, 0}); + auto b = g.addNd(POINT{0, 10}); + auto c = g.addNd(POINT{10, 0}); + auto d = g.addNd(POINT{20, 0}); + + a->pl().setComp(1); + b->pl().setComp(1); + c->pl().setComp(1); + d->pl().setComp(1); + + auto eA = g.addEdg(a, c); + auto eB = g.addEdg(b, c); + auto eC = g.addEdg(c, d); + + eA->pl().setCost(10); + eB->pl().setCost(6); + eC->pl().setCost(100); + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(10)); + TEST(cmGet(costM, 1, 0), ==, approx(6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0.5, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(50 + 10)); + TEST(cmGet(costM, 1, 0), ==, approx(50 + 6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), ==, approx(2)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0.9, {}, 0, {}}); + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(90 + 5)); + TEST(cmGet(costM, 1, 0), ==, approx(90 + 2)); + } + + // with hopsfast + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0.5, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(50 + 6)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(2)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}}); + tos.push_back({eC, 0, 0.9, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), >=, maxTime); + TEST(cmGet(costM, 1, 0), ==, approx(90 + 2)); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{0, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{9999, 0}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 0, {}, 0, {}}); + + LayerCostsDAG initCost{6, 0, 20}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + // we also get this, because the edge is the same! + TEST(cmGet(costM, 0, 0), ==, approx(5)); + TEST(cmGet(costM, 1, 0), ==, approx(10)); + TEST(cmGet(costM, 2, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + tos.push_back({eC, 0, 1, {}, 0, {}}); + + LayerCostsDAG initCost{6, 0, 20}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + // we also get this, because the edge is the same! + TEST(cmGet(costM, 0, 0), ==, approx(5 + 100)); + TEST(cmGet(costM, 1, 0), ==, approx(10 + 100)); + TEST(cmGet(costM, 2, 0), >=, maxTime); + } + + { + EdgeCandGroup froms, tos; + CostMatrix costM, dists; + froms.push_back({eA, 0, 0.5, {}, 0, {}}); + froms.push_back({eA, 0, 0, {}, 0, {}}); + froms.push_back({eB, 0, 0, {}, 0, {}}); + + tos.push_back({eC, 0, 1, {}, 0, {}}); + tos.push_back({eC, 0, 0.5, {}, 0, {}}); + + LayerCostsDAG initCost{6, 0, 20}; + + double maxTime = 9999; + + pfaedle::router::HopCache c; + + router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c, + maxTime); + + // we also get this, because the edge is the same! + TEST(cmGet(costM, 0, 0), ==, approx(5 + 100)); + TEST(cmGet(costM, 1, 0), ==, approx(10 + 100)); + TEST(cmGet(costM, 0, 1), ==, approx(5 + 50)); + TEST(cmGet(costM, 1, 1), ==, approx(10 + 50)); + TEST(cmGet(costM, 2, 0), >=, maxTime); + TEST(cmGet(costM, 2, 1), >=, maxTime); + } + + exit(0); +} diff --git a/src/pfaedle/trgraph/EdgePL.cpp b/src/pfaedle/trgraph/EdgePL.cpp index cd6c241..7c5b613 100644 --- a/src/pfaedle/trgraph/EdgePL.cpp +++ b/src/pfaedle/trgraph/EdgePL.cpp @@ -11,15 +11,12 @@ using pfaedle::trgraph::EdgePL; using pfaedle::trgraph::TransitEdgeLine; - std::map EdgePL::_flines; std::map EdgePL::_tlines; // _____________________________________________________________________________ EdgePL::EdgePL() - : _length(0), _oneWay(0), _hasRestr(false), _rev(false), _lvl(0) { - _l = new LINE(); - _flines[_l] = 1; + : _oneWay(0), _hasRestr(false), _rev(false), _lvl(0), _cost(0), _l(0) { } // _____________________________________________________________________________ @@ -27,17 +24,20 @@ EdgePL::EdgePL(const EdgePL& pl) : EdgePL(pl, false) {} // _____________________________________________________________________________ EdgePL::EdgePL(const EdgePL& pl, bool geoflat) - : _length(pl._length), - _oneWay(pl._oneWay), + : _oneWay(pl._oneWay), _hasRestr(pl._hasRestr), _rev(pl._rev), - _lvl(pl._lvl) { - if (geoflat) { - _l = pl._l; - } else { - _l = new LINE(*pl._l); + _lvl(pl._lvl), + _cost(pl._cost), + _l(0) { + if (pl._l) { + if (geoflat) { + _l = pl._l; + } else { + _l = new LINE(*pl._l); + } + _flines[_l]++; } - _flines[_l]++; for (auto l : pl._lines) addLine(l); } @@ -75,16 +75,23 @@ EdgePL EdgePL::revCopy() const { } // _____________________________________________________________________________ -void EdgePL::setLength(double d) { _length = d; } +double EdgePL::getLength() const { + double len = 0; -// _____________________________________________________________________________ -double EdgePL::getLength() const { return _length; } + for (size_t i = 1; i < _l->size(); i++) { + len += haversine((*_l)[i-1], (*_l)[i]); + } + + return len; +} // _____________________________________________________________________________ void EdgePL::addLine(const TransitEdgeLine* l) { - if (std::find(_lines.begin(), _lines.end(), l) == _lines.end()) { + auto lb = std::lower_bound(_lines.begin(), _lines.end(), l); + if (lb == _lines.end() || *lb != l) { _lines.reserve(_lines.size() + 1); - _lines.push_back(l); + lb = std::lower_bound(_lines.begin(), _lines.end(), l); + _lines.insert(lb, l); if (_tlines.count(l)) _tlines[l]++; else @@ -103,7 +110,13 @@ const std::vector& EdgePL::getLines() const { } // _____________________________________________________________________________ -void EdgePL::addPoint(const POINT& p) { _l->push_back(p); } +void EdgePL::addPoint(const POINT& p) { + if (!_l) { + _l = new LINE(); + _flines[_l] = 1; + } + _l->push_back(p); +} // _____________________________________________________________________________ const LINE* EdgePL::getGeom() const { return _l; } @@ -114,8 +127,9 @@ LINE* EdgePL::getGeom() { return _l; } // _____________________________________________________________________________ util::json::Dict EdgePL::getAttrs() const { util::json::Dict obj; - obj["m_length"] = std::to_string(_length); + obj["m_length"] = std::to_string(getLength()); obj["oneway"] = std::to_string(static_cast(_oneWay)); + obj["cost"] = std::to_string(static_cast(_cost) / 10.0); obj["level"] = std::to_string(_lvl); obj["restriction"] = isRestricted() ? "yes" : "no"; @@ -152,10 +166,10 @@ void EdgePL::setOneWay(uint8_t dir) { _oneWay = dir; } void EdgePL::setOneWay() { _oneWay = 1; } // _____________________________________________________________________________ -void EdgePL::setLvl(uint8_t lvl) { _lvl = lvl; } +uint32_t EdgePL::getCost() const { return _cost; } // _____________________________________________________________________________ -uint8_t EdgePL::lvl() const { return _lvl; } +void EdgePL::setCost(uint32_t c) { _cost = c; } // _____________________________________________________________________________ void EdgePL::setRev() { _rev = true; } diff --git a/src/pfaedle/trgraph/EdgePL.h b/src/pfaedle/trgraph/EdgePL.h index 368ac2f..3c71fea 100644 --- a/src/pfaedle/trgraph/EdgePL.h +++ b/src/pfaedle/trgraph/EdgePL.h @@ -16,8 +16,6 @@ using util::geograph::GeoEdgePL; - - namespace pfaedle { namespace trgraph { @@ -28,14 +26,17 @@ struct TransitEdgeLine { std::string fromStr; std::string toStr; std::string shortName; + uint32_t color; }; inline bool operator==(const TransitEdgeLine& a, const TransitEdgeLine& b) { + // ignoring color here! return a.fromStr == b.fromStr && a.toStr == b.toStr && a.shortName == b.shortName; } inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) { + // ignoring color here! return a.fromStr < b.fromStr || (a.fromStr == b.fromStr && a.toStr < b.toStr) || (a.fromStr == b.fromStr && a.toStr == b.toStr && @@ -65,14 +66,20 @@ class EdgePL { // Return the length in meters stored for this edge payload double getLength() const; - // Set the length in meters for this edge payload - void setLength(double d); - // Set this edge as a one way node, either in the default direction of // the edge (no arg), or the direction specified in dir void setOneWay(); void setOneWay(uint8_t dir); + void setLvl(uint8_t lvl) { assert(lvl < 9); _lvl = lvl; } + uint8_t lvl() const { return _lvl; } + + // Return the cost for this edge payload + uint32_t getCost() const; + + // Set the cost for this edge payload + void setCost(uint32_t d); + // Mark this payload' edge as having some restrictions void setRestricted(); @@ -85,12 +92,6 @@ class EdgePL { // True if this edge is restricted bool isRestricted() const; - // Set the level of this edge. - void setLvl(uint8_t lvl); - - // Return the level of this edge. - uint8_t lvl() const; - // Return the one-way code stored for this edge. uint8_t oneWay() const; @@ -115,11 +116,11 @@ class EdgePL { EdgePL revCopy() const; private: - float _length; uint8_t _oneWay : 2; bool _hasRestr : 1; bool _rev : 1; - uint8_t _lvl : 3; + uint8_t _lvl: 4; + uint32_t _cost; // costs in 1/10th seconds LINE* _l; diff --git a/src/pfaedle/trgraph/Graph.h b/src/pfaedle/trgraph/Graph.h index 7e3baba..7f7d50a 100644 --- a/src/pfaedle/trgraph/Graph.h +++ b/src/pfaedle/trgraph/Graph.h @@ -24,8 +24,8 @@ namespace trgraph { typedef util::graph::Edge Edge; typedef util::graph::Node Node; typedef util::graph::DirGraph Graph; -typedef Grid NodeGrid; -typedef Grid EdgeGrid; +typedef Grid NodeGrid; +typedef Grid EdgeGrid; } // namespace trgraph } // namespace pfaedle diff --git a/src/pfaedle/trgraph/NodePL.cpp b/src/pfaedle/trgraph/NodePL.cpp index a0aec0a..b4bba8b 100644 --- a/src/pfaedle/trgraph/NodePL.cpp +++ b/src/pfaedle/trgraph/NodePL.cpp @@ -3,22 +3,19 @@ // Authors: Patrick Brosi #include +#include +#include #include #include "pfaedle/trgraph/NodePL.h" -#include "pfaedle/trgraph/StatGroup.h" #include "pfaedle/trgraph/StatInfo.h" #include "util/String.h" -using pfaedle::trgraph::StatInfo; -using pfaedle::trgraph::NodePL; using pfaedle::trgraph::Component; +using pfaedle::trgraph::NodePL; +using pfaedle::trgraph::StatInfo; -// we use the adress of this dummy station info as a special value -// of this node, meaning "is a station block". Re-using the _si field here -// saves some memory -StatInfo NodePL::_blockerSI = StatInfo(); - -std::unordered_map NodePL::_comps; +std::vector NodePL::comps; +std::vector NodePL::_statInfos; // _____________________________________________________________________________ NodePL::NodePL() @@ -32,19 +29,6 @@ NodePL::NodePL() { } -// _____________________________________________________________________________ -NodePL::NodePL(const NodePL& pl) - : _geom(pl._geom), - _si(0), - _component(pl._component) -#ifdef PFAEDLE_DBG - , - _vis(pl._vis) -#endif -{ - if (pl._si) setSI(*(pl._si)); -} - // _____________________________________________________________________________ NodePL::NodePL(const POINT& geom) : _geom(geom), @@ -70,18 +54,6 @@ NodePL::NodePL(const POINT& geom, const StatInfo& si) setSI(si); } -// _____________________________________________________________________________ -NodePL::~NodePL() { - if (getSI()) delete _si; - if (_component) { - _comps[_component]--; - if (_comps[_component] == 0) { - delete _component; - _comps.erase(_comps.find(_component)); - } - } -} - // _____________________________________________________________________________ void NodePL::setVisited() const { #ifdef PFAEDLE_DBG @@ -93,18 +65,14 @@ void NodePL::setVisited() const { void NodePL::setNoStat() { _si = 0; } // _____________________________________________________________________________ -const Component* NodePL::getComp() const { return _component; } +const Component& NodePL::getComp() const { return comps[_component - 1]; } // _____________________________________________________________________________ -void NodePL::setComp(const Component* c) { - if (_component == c) return; - _component = c; +uint32_t NodePL::getCompId() const { return _component; } - // NOT thread safe! - if (!_comps.count(c)) - _comps[c] = 1; - else - _comps[c]++; +// _____________________________________________________________________________ +void NodePL::setComp(uint32_t id) { + _component = id; } // _____________________________________________________________________________ @@ -116,54 +84,59 @@ void NodePL::setGeom(const POINT& geom) { _geom = geom; } // _____________________________________________________________________________ util::json::Dict NodePL::getAttrs() const { util::json::Dict obj; - obj["component"] = std::to_string(reinterpret_cast(_component)); + obj["component"] = std::to_string(_component); #ifdef PFAEDLE_DBG obj["dijkstra_vis"] = _vis ? "yes" : "no"; #endif if (getSI()) { obj["station_info_ptr"] = util::toString(_si); - obj["station_name"] = _si->getName(); - obj["station_alt_names"] = util::implode(_si->getAltNames(), ","); - obj["from_osm"] = _si->isFromOsm() ? "yes" : "no"; - obj["station_platform"] = _si->getTrack(); - obj["station_group"] = - std::to_string(reinterpret_cast(_si->getGroup())); + obj["station_name"] = getSI()->getName(); + obj["station_alt_names"] = + util::implode(getSI()->getAltNames(), ","); + obj["station_platform"] = getSI()->getTrack(); #ifdef PFAEDLE_STATION_IDS // only print this in debug mode - obj["station_id"] = _si->getId(); + obj["station_id"] = getSI()->getId(); #endif - - - std::stringstream gtfsIds; - if (_si->getGroup()) { - for (auto* s : _si->getGroup()->getStops()) { - gtfsIds << s->getId() << " (" << s->getName() << "),"; - } - } - - obj["station_group_stops"] = gtfsIds.str(); } return obj; } // _____________________________________________________________________________ -void NodePL::setSI(const StatInfo& si) { _si = new StatInfo(si); } +void NodePL::setSI(const StatInfo& si) { + _statInfos.emplace_back(si); + _si = _statInfos.size(); +} // _____________________________________________________________________________ const StatInfo* NodePL::getSI() const { if (isBlocker()) return 0; - return _si; + if (isTurnCycle()) return 0; + if (_si == 0) return 0; + return &_statInfos[_si - 1]; } // _____________________________________________________________________________ StatInfo* NodePL::getSI() { if (isBlocker()) return 0; - return _si; + if (isTurnCycle()) return 0; + if (_si == 0) return 0; + return &_statInfos[_si - 1]; } // _____________________________________________________________________________ -void NodePL::setBlocker() { _si = &_blockerSI; } +void NodePL::setTurnCycle() { _si = std::numeric_limits::max() - 1; } // _____________________________________________________________________________ -bool NodePL::isBlocker() const { return _si == &_blockerSI; } +bool NodePL::isTurnCycle() const { + return _si == (std::numeric_limits::max() - 1); +} + +// _____________________________________________________________________________ +void NodePL::setBlocker() { _si = std::numeric_limits::max(); } + +// _____________________________________________________________________________ +bool NodePL::isBlocker() const { + return _si == std::numeric_limits::max(); +} diff --git a/src/pfaedle/trgraph/NodePL.h b/src/pfaedle/trgraph/NodePL.h index 7c1cdb0..8d4efa9 100644 --- a/src/pfaedle/trgraph/NodePL.h +++ b/src/pfaedle/trgraph/NodePL.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "ad/cppgtfs/gtfs/Feed.h" #include "pfaedle/Def.h" #include "pfaedle/trgraph/StatInfo.h" @@ -20,7 +21,7 @@ namespace pfaedle { namespace trgraph { struct Component { - uint8_t minEdgeLvl : 3; + float maxSpeed; }; /* @@ -29,10 +30,8 @@ struct Component { class NodePL { public: NodePL(); - NodePL(const NodePL& pl); // NOLINT NodePL(const POINT& geom); // NOLINT NodePL(const POINT& geom, const StatInfo& si); - ~NodePL(); // Return the geometry of this node. const POINT* getGeom() const; @@ -52,10 +51,13 @@ class NodePL { void setNoStat(); // Get the component of this node - const Component* getComp() const; + const Component& getComp() const; + + // Get the component of this node + uint32_t getCompId() const; // Set the component of this node - void setComp(const Component* c); + void setComp(uint32_t c); // Make this node a blocker void setBlocker(); @@ -63,21 +65,27 @@ class NodePL { // Check if this node is a blocker bool isBlocker() const; + // Make this node a turning cycle + void setTurnCycle(); + + // Check if this node is a blocker + bool isTurnCycle() const; + // Mark this node as visited (usefull for counting search space in Dijkstra) // (only works for DEBUG build type) void setVisited() const; + static std::vector comps; + private: POINT _geom; - StatInfo* _si; - const Component* _component; + uint32_t _si; + uint32_t _component; #ifdef PFAEDLE_DBG mutable bool _vis; #endif - - static StatInfo _blockerSI; - static std::unordered_map _comps; + static std::vector _statInfos; }; } // namespace trgraph } // namespace pfaedle diff --git a/src/pfaedle/trgraph/Normalizer.cpp b/src/pfaedle/trgraph/Normalizer.cpp index 5e254a0..5daaa80 100644 --- a/src/pfaedle/trgraph/Normalizer.cpp +++ b/src/pfaedle/trgraph/Normalizer.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -37,17 +36,6 @@ Normalizer& Normalizer::operator=(Normalizer other) { return *this; } -// _____________________________________________________________________________ -std::string Normalizer::operator()(std::string sn) const { - return normTS(sn); -} - -// _____________________________________________________________________________ -std::string Normalizer::normTS(const std::string& sn) const { - std::lock_guard lock(_mutex); - return norm(sn); -} - // _____________________________________________________________________________ std::string Normalizer::norm(const std::string& sn) const { auto i = _cache.find(sn); diff --git a/src/pfaedle/trgraph/Normalizer.h b/src/pfaedle/trgraph/Normalizer.h index 6546963..ae02d5a 100644 --- a/src/pfaedle/trgraph/Normalizer.h +++ b/src/pfaedle/trgraph/Normalizer.h @@ -10,7 +10,6 @@ #include #include #include -#include namespace pfaedle { namespace trgraph { @@ -37,19 +36,13 @@ class Normalizer { // Normalize sn, not thread safe std::string norm(const std::string& sn) const; - // Normalize sn, thread safe - std::string normTS(const std::string& sn) const; - // Normalize sn based on the rules of this normalizer, uses the thread safe - // version of norm() internally - std::string operator()(std::string sn) const; bool operator==(const Normalizer& b) const; private: ReplRulesComp _rules; ReplRules _rulesOrig; mutable std::unordered_map _cache; - mutable std::mutex _mutex; void buildRules(const ReplRules& rules); }; diff --git a/src/pfaedle/trgraph/StatGroup.cpp b/src/pfaedle/trgraph/StatGroup.cpp deleted file mode 100644 index 75b45d4..0000000 --- a/src/pfaedle/trgraph/StatGroup.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#include -#include "pfaedle/trgraph/StatGroup.h" -#include "util/geo/Geo.h" - -using pfaedle::trgraph::StatGroup; -using pfaedle::trgraph::Node; -using pfaedle::router::NodeCandGroup; -using ad::cppgtfs::gtfs::Stop; - -// _____________________________________________________________________________ -StatGroup::StatGroup() {} - -// _____________________________________________________________________________ -void StatGroup::addStop(const Stop* s) { _stops.insert(s); } - -// _____________________________________________________________________________ -void StatGroup::addNode(trgraph::Node* n) { _nodes.insert(n); } - -// _____________________________________________________________________________ -void StatGroup::merge(StatGroup* other) { - if (other == this) return; - - std::set nds = other->getNodes(); - std::set stops = other->getStops(); - - for (auto on : nds) { - on->pl().getSI()->setGroup(this); - addNode(on); - } - - for (auto* os : stops) { - addStop(os); - } -} - -// _____________________________________________________________________________ -const NodeCandGroup& StatGroup::getNodeCands(const Stop* s) const { - return _stopNodePens.at(s); -} - -// _____________________________________________________________________________ -const std::set& StatGroup::getNodes() const { return _nodes; } - -// _____________________________________________________________________________ -void StatGroup::remNode(trgraph::Node* n) { - auto it = _nodes.find(n); - if (it != _nodes.end()) _nodes.erase(it); -} - -// _____________________________________________________________________________ -std::set& StatGroup::getNodes() { return _nodes; } - -// _____________________________________________________________________________ -const std::set& StatGroup::getStops() const { return _stops; } - -// _____________________________________________________________________________ -double StatGroup::getPen(const Stop* s, trgraph::Node* n, - const trgraph::Normalizer& platformNorm, - double trackPen, double distPenFac, - double nonOsmPen) const { - POINT p = - util::geo::latLngToWebMerc(s->getLat(), s->getLng()); - - double distPen = util::geo::webMercMeterDist(p, *n->pl().getGeom()); - distPen *= distPenFac; - - std::string platform = platformNorm.norm(s->getPlatformCode()); - - if (!platform.empty() && !n->pl().getSI()->getTrack().empty() && - n->pl().getSI()->getTrack() == platform) { - trackPen = 0; - } - - if (n->pl().getSI()->isFromOsm()) nonOsmPen = 0; - - return distPen + trackPen + nonOsmPen; -} - -// _____________________________________________________________________________ -void StatGroup::writePens(const trgraph::Normalizer& platformNorm, - double trackPen, double distPenFac, - double nonOsmPen) { - if (_stopNodePens.size()) return; // already written - for (auto* s : _stops) { - for (auto* n : _nodes) { - _stopNodePens[s].push_back(router::NodeCand{ - n, getPen(s, n, platformNorm, trackPen, distPenFac, nonOsmPen)}); - } - } -} diff --git a/src/pfaedle/trgraph/StatGroup.h b/src/pfaedle/trgraph/StatGroup.h deleted file mode 100644 index a3341af..0000000 --- a/src/pfaedle/trgraph/StatGroup.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Patrick Brosi - -#ifndef PFAEDLE_TRGRAPH_STATGROUP_H_ -#define PFAEDLE_TRGRAPH_STATGROUP_H_ - -#include -#include -#include -#include "ad/cppgtfs/gtfs/Feed.h" -#include "pfaedle/router/Router.h" -#include "pfaedle/trgraph/Graph.h" -#include "pfaedle/trgraph/Normalizer.h" - -namespace pfaedle { -namespace trgraph { - -using ad::cppgtfs::gtfs::Stop; - -/* - * A group of stations that belong together semantically (for example, multiple - * stop points of a larger bus station) - */ -class StatGroup { - public: - StatGroup(); - StatGroup(const StatGroup& a) = delete; - - // Add a stop s to this station group - void addStop(const Stop* s); - - // Add a node n to this station group - void addNode(trgraph::Node* n); - - // Return all nodes contained in this group - const std::set& getNodes() const; - std::set& getNodes(); - - // Return all stops contained in this group - const std::set& getStops() const; - - // Remove a node from this group - void remNode(trgraph::Node* n); - - // All nodes in other will be in this group, their SI's updated, and the - // "other" group deleted. - void merge(StatGroup* other); - - // Return node candidates for stop s from this group - const router::NodeCandGroup& getNodeCands(const Stop* s) const; - - // Write the penalties for all stops contained in this group so far. - void writePens(const trgraph::Normalizer& platformNorm, double trackPen, - double distPenFac, double nonOsmPen); - - private: - std::set _nodes; - std::set _stops; - - // for each stop in this group, a penalty for each of the nodes here, based on - // its distance and optionally the track number - std::unordered_map _stopNodePens; - - double getPen(const Stop* s, trgraph::Node* n, - const trgraph::Normalizer& norm, double trackPen, - double distPenFac, double nonOsmPen) const; -}; -} // namespace trgraph -} // namespace pfaedle - -#endif // PFAEDLE_TRGRAPH_STATGROUP_H_ diff --git a/src/pfaedle/trgraph/StatInfo.cpp b/src/pfaedle/trgraph/StatInfo.cpp index 0b642b6..132c985 100644 --- a/src/pfaedle/trgraph/StatInfo.cpp +++ b/src/pfaedle/trgraph/StatInfo.cpp @@ -3,66 +3,24 @@ // Authors: Patrick Brosi #include "pfaedle/router/Comp.h" -#include "pfaedle/trgraph/StatGroup.h" #include "pfaedle/trgraph/StatInfo.h" using pfaedle::trgraph::StatInfo; -using pfaedle::trgraph::StatGroup; - -std::unordered_map StatInfo::_groups; // _____________________________________________________________________________ -StatInfo::StatInfo() : _name(""), _track(""), _fromOsm(false), _group(0) {} +StatInfo::StatInfo() : _name(""), _track("") {} // _____________________________________________________________________________ StatInfo::StatInfo(const StatInfo& si) - : _name(si._name), - _altNames(si._altNames), - _track(si._track), - _fromOsm(si._fromOsm), - _group(0) { - setGroup(si._group); + : _name(si._name), _altNames(si._altNames), _track(si._track) { #ifdef PFAEDLE_STATION_IDS _id = si._id; #endif } // _____________________________________________________________________________ -StatInfo::StatInfo(const std::string& name, const std::string& track, - bool fromOsm) - : _name(name), _track(track), _fromOsm(fromOsm), _group(0) {} - -// _____________________________________________________________________________ -StatInfo::~StatInfo() { unRefGroup(_group); } - -// _____________________________________________________________________________ -void StatInfo::unRefGroup(StatGroup* g) { - if (g) { - _groups[g]--; - if (_groups[g] == 0) { - // std::cout << "Deleting " << g << std::endl; - delete g; - _groups.erase(_groups.find(g)); - } - } -} - -// _____________________________________________________________________________ -void StatInfo::setGroup(StatGroup* g) { - if (_group == g) return; - unRefGroup(_group); - - _group = g; - - // NOT thread safe! - if (!_groups.count(g)) - _groups[g] = 1; - else - _groups[g]++; -} - -// _____________________________________________________________________________ -StatGroup* StatInfo::getGroup() const { return _group; } +StatInfo::StatInfo(const std::string& name, const std::string& track) + : _name(name), _track(track) {} // _____________________________________________________________________________ const std::string& StatInfo::getName() const { return _name; } @@ -70,12 +28,6 @@ const std::string& StatInfo::getName() const { return _name; } // _____________________________________________________________________________ const std::string& StatInfo::getTrack() const { return _track; } -// _____________________________________________________________________________ -bool StatInfo::isFromOsm() const { return _fromOsm; } - -// _____________________________________________________________________________ -void StatInfo::setIsFromOsm(bool is) { _fromOsm = is; } - // _____________________________________________________________________________ double StatInfo::simi(const StatInfo* other) const { if (!other) return 0; diff --git a/src/pfaedle/trgraph/StatInfo.h b/src/pfaedle/trgraph/StatInfo.h index de0bbcf..dcd5ba7 100644 --- a/src/pfaedle/trgraph/StatInfo.h +++ b/src/pfaedle/trgraph/StatInfo.h @@ -6,24 +6,20 @@ #define PFAEDLE_TRGRAPH_STATINFO_H_ #include -#include #include +#include namespace pfaedle { namespace trgraph { -// forward declaration -class StatGroup; - /* - * Meta information (name, alternative names, track, group...) of a single stop + * Meta information (name, alternative names, track, ...) of a single stop */ class StatInfo { public: StatInfo(); StatInfo(const StatInfo& si); - StatInfo(const std::string& name, const std::string& track, bool _fromOsm); - ~StatInfo(); + StatInfo(const std::string& name, const std::string& track); // Return this stops names. const std::string& getName() const; @@ -43,18 +39,6 @@ class StatInfo { // Return the similarity between this stop and other double simi(const StatInfo* other) const; - // Set this stations group. - void setGroup(StatGroup* g); - - // Return this stations group. - StatGroup* getGroup() const; - - // True if this stop was from osm - bool isFromOsm() const; - - // Set this stop as coming from osm - void setIsFromOsm(bool is); - #ifdef PFAEDLE_STATION_IDS const std::string& getId() const { return _id; } void setId(const std::string& id) { _id = id; } @@ -64,17 +48,12 @@ class StatInfo { std::string _name; std::vector _altNames; std::string _track; - bool _fromOsm; - StatGroup* _group; #ifdef PFAEDLE_STATION_IDS // debug feature to store station ids from both OSM // and GTFS std::string _id; #endif - - static std::unordered_map _groups; - static void unRefGroup(StatGroup* g); }; } // namespace trgraph } // namespace pfaedle diff --git a/src/shapevl/CMakeLists.txt b/src/shapevl/CMakeLists.txt new file mode 100644 index 0000000..e6bdc17 --- /dev/null +++ b/src/shapevl/CMakeLists.txt @@ -0,0 +1,15 @@ +file(GLOB_RECURSE shapevl_SRC *.cpp) + +set(shapevl_main ShapevlMain.cpp) + +list(REMOVE_ITEM shapevl_SRC ${shapevl_main}) + +include_directories( + ${PFAEDLE_INCLUDE_DIR} +) + +add_executable(shapevl ${shapevl_main}) +add_library(shapevl_dep ${shapevl_SRC}) + +include_directories(shapevl_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src) +target_link_libraries(shapevl shapevl_dep util ad_cppgtfs -lpthread) diff --git a/src/shapevl/Collector.cpp b/src/shapevl/Collector.cpp new file mode 100644 index 0000000..610192d --- /dev/null +++ b/src/shapevl/Collector.cpp @@ -0,0 +1,373 @@ +// Copyright 2018, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include +#include "ad/cppgtfs/gtfs/Feed.h" +#include "pfaedle/Def.h" +#include "shapevl/Collector.h" +#include "shapevl/Result.h" +#include "util/geo/Geo.h" +#include "util/geo/PolyLine.h" +#include "util/geo/output/GeoJsonOutput.h" +#include "util/log/Log.h" + +using util::geo::PolyLine; + +using ad::cppgtfs::gtfs::Shape; +using ad::cppgtfs::gtfs::Trip; +using pfaedle::eval::Collector; +using pfaedle::eval::Result; +using util::geo::output::GeoJsonOutput; + +// _____________________________________________________________________________ +double Collector::add(const Trip* oldT, const Shape* oldS, const Trip* newT, + const Shape* newS) { + // This adds a new trip with a new shape to our evaluation. + + _trips++; + + if (!oldS) { + // If there is no original shape, we cannot compare them - abort! + _noOrigShp++; + return 0; + } + + for (auto st : oldT->getStopTimes()) { + if (st.getShapeDistanceTravelled() < 0) { + // we cannot safely compare trips without shape dist travelled + // information - abort! + _noOrigShp++; + return 0; + } + } + + for (auto st : newT->getStopTimes()) { + if (st.getShapeDistanceTravelled() < 0) { + // we cannot safely compare trips without shape dist travelled + // information - abort! + _noOrigShp++; + return 0; + } + } + + double fd = 0; + + // A "segment" is a path from station s_i to station s_{i+1} + + size_t unmatchedSegments; // number of unmatched segments + double unmatchedSegmentsLength; // total _acc. length of unmatched segments + + std::vector oldDists; + LINE oldL = getWebMercLine(oldS, &oldDists); + + std::vector newDists; + LINE newL = getWebMercLine(newS, &newDists); + + auto oldSegs = segmentize(oldT, oldL, oldDists); + auto newSegs = segmentize(newT, newL, newDists); + + // new lines build from cleaned-up shapes + LINE oldLCut; + LINE newLCut; + + for (auto oldL : oldSegs) + oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end()); + + for (auto newL : newSegs) + newLCut.insert(newLCut.end(), newL.begin(), newL.end()); + + // determine the scale factor between the distance in projected + // coordinates and the real-world distance in meters + auto avgY = + (oldSegs.front().front().getY() + oldSegs.back().back().getY()) / 2; + double fac = cos(2 * atan(exp(avgY / 6378137.0)) - 1.5707965); + + double SEGL = 10; + + if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS->getId())) { + fd = _dCache[oldS][newS->getId()]; + } else { + fd = util::geo::accFrechetDistC(oldLCut, newLCut, SEGL / fac) * fac; + _dCache[oldS][newS->getId()] = fd; + } + + if (_dACache.count(oldS) && + _dACache.find(oldS)->second.count(newS->getId())) { + unmatchedSegments = _dACache[oldS][newS->getId()].first; + unmatchedSegmentsLength = _dACache[oldS][newS->getId()].second; + } else { + auto dA = getDa(oldSegs, newSegs); + _dACache[oldS][newS->getId()] = dA; + unmatchedSegments = dA.first; + unmatchedSegmentsLength = dA.second; + } + + double totL = 0; + for (auto l : oldSegs) totL += util::geo::len(l) * fac; + + // filter out shapes with a length of under 5 meters - they are most likely + // artifacts + if (totL < 5) { + _noOrigShp++; + return 0; + } + + _fdSum += fd / totL; + _unmatchedSegSum += unmatchedSegments; + _unmatchedSegLengthSum += unmatchedSegmentsLength; + + double avgFd = fd / totL; + double AN = static_cast(unmatchedSegments) / + static_cast(oldSegs.size()); + double AL = unmatchedSegmentsLength / totL; + + _results.insert(Result(oldT, avgFd)); + + if (AN <= 0.0001) _acc0++; + if (AN <= 0.1) _acc10++; + if (AN <= 0.2) _acc20++; + if (AN <= 0.4) _acc40++; + if (AN <= 0.8) _acc80++; + + LOG(VDEBUG) << "This result (" << oldT->getId() + << "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size() + << " = " << AN << " A_L/L = " << unmatchedSegmentsLength << "/" + << totL << " = " << AL << " d_f = " << avgFd; + + if (_reportOut) { + (*_reportOut) << oldT->getId() << "\t" << AN << "\t" << AL << "\t" << avgFd + << "\t" << util::geo::getWKT(oldSegs) << "\t" + << util::geo::getWKT(newSegs) << "\n"; + } + + return avgFd; +} + +// _____________________________________________________________________________ +std::vector Collector::segmentize(const Trip* t, const LINE& shape, + const std::vector& dists) { + // The straightforward way to segmentize the shape would be to just cut it at + // the exact measurements in stop_times.txt. We have tried that, but found + // that it produces misleading results for the following reason: + // + // 1) The measurement specifies an exact position on the shape. + // 2) Even if we consider correct rail or bus tracks, the "right" position + // where a vehicle may come to a halt is not a point - its a line segment, + // basically the entire track in railroad term + // 3) The position point on the shape in real-world feeds may be either a) the + // position where a train comes to a halt, b) the position where a carriage + // comes to a halt, c) the beginning of the tracks line segment, d) the end + // of the tracks line segment, e) the center of the tracks line segment, f) + // ... (any position on the tracks line segment. + // 4) The "correct" position is NOT well-defined. + // 5) As tracks are often longer than 20 meters, this will dillute our AN + // measure, although the shape is CORRECT (because the ground truth uses + // a different position philosophy than the test data) + // 6) To normalize this, we always the following approach: + // a) Get the exact progression of the measurment on the shape + // b) Extract a segment of 200 meters, with the measurement progress in the middle + // c) Project the GROUND TRUTH station coordinate to this segment + // d) The result is the cutting point + // 7) If a completely wrong track was chosen, the frechet distance will still + // be greater than 20 meters and AN will measure an unmatch. + // 8) TODO: implement this, explain this in diss + std::vector ret; + + if (t->getStopTimes().size() < 2) return ret; + + POLYLINE pl(shape); + std::vector > cuts; + + size_t i = 0; + for (auto st : t->getStopTimes()) { + cuts.push_back(std::pair( + util::geo::latLngToWebMerc(st.getStop()->getLat(), + st.getStop()->getLng()), + st.getShapeDistanceTravelled())); + i++; + } + + // get first half of geometry, and search for start point there! + size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) - + dists.begin(); + if (before + 1 > shape.size()) before = shape.size() - 1; + assert(shape.begin() + before + 1 <= shape.end()); + POLYLINE l(LINE(shape.begin(), shape.begin() + before + 1)); + auto lastLp = l.projectOn(cuts.front().first); + + for (size_t i = 1; i < cuts.size(); i++) { + size_t before = shape.size(); + if (i < cuts.size() - 1 && cuts[i + 1].second > -0.5) { + before = + std::upper_bound(dists.begin(), dists.end(), cuts[i + 1].second) - + dists.begin(); + } + + POLYLINE beforePl(LINE(shape.begin(), shape.begin() + before)); + + auto curLp = beforePl.projectOnAfter(cuts[i].first, lastLp.lastIndex); + + ret.push_back(pl.getSegment(lastLp, curLp).getLine()); + lastLp = curLp; + } + + return ret; +} + +// _____________________________________________________________________________ +LINE Collector::getWebMercLine(const Shape* s, std::vector* dists) { + LINE ret; + + for (size_t i = 0; i < s->getPoints().size(); i++) { + ret.push_back(util::geo::latLngToWebMerc(s->getPoints()[i].lat, + s->getPoints()[i].lng)); + (*dists).push_back(s->getPoints()[i].travelDist); + } + + return ret; +} + +// _____________________________________________________________________________ +const std::set& Collector::getResults() const { return _results; } + +// _____________________________________________________________________________ +double Collector::getAvgDist() const { return _fdSum / _results.size(); } + +// _____________________________________________________________________________ +std::vector Collector::getBins(double mind, double maxd, size_t steps) { + double bin = (maxd - mind) / steps; + double curE = mind + bin; + + std::vector ret; + while (curE <= maxd) { + ret.push_back(curE); + curE += bin; + } + return ret; +} + +// _____________________________________________________________________________ +void Collector::printCsv(std::ostream* os, + const std::set& result) const { + for (auto r : result) (*os) << r.getDist() << "\n"; +} + +// _____________________________________________________________________________ +double Collector::getAcc() const { + return static_cast(_acc0) / static_cast(_results.size()); +} + +// _____________________________________________________________________________ +void Collector::printShortStats(std::ostream* os) const { + if (_results.size()) { + (*os) << "acc-0: " + << (static_cast(_acc0) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-10: " + << (static_cast(_acc10) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-20: " + << (static_cast(_acc20) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-40: " + << (static_cast(_acc40) / + static_cast(_results.size())) * + 100 + << " %"; + (*os) << " acc-80: " + << (static_cast(_acc80) / + static_cast(_results.size())) * + 100 + << " %"; + } +} + +// _____________________________________________________________________________ +void Collector::printStats(std::ostream* os) const { + (*os) << std::setfill(' ') << std::setw(50) << " # of trips: " << _trips + << "\n"; + (*os) << std::setfill(' ') << std::setw(50) + << " # of trips new shapes were matched for: " << _results.size() + << "\n"; + (*os) << std::setw(50) << " # of trips without input shapes: " << _noOrigShp + << "\n"; + + if (_results.size()) { + (*os) << std::setw(50) << " highest avg frechet distance to input shapes: " + << (--_results.end())->getDist() << " (on trip #" + << (--_results.end())->getTrip()->getId() << ")\n"; + (*os) << std::setw(50) << " lowest distance to input shapes: " + << (_results.begin())->getDist() << " (on trip #" + << (_results.begin())->getTrip()->getId() << ")\n"; + (*os) << std::setw(50) + << " averaged avg frechet distance: " << getAvgDist() << "\n"; + + (*os) << "\n"; + (*os) << " acc-0: " + << (static_cast(_acc0) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-10: " + << (static_cast(_acc10) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-20: " + << (static_cast(_acc20) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-40: " + << (static_cast(_acc40) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + (*os) << " acc-80: " + << (static_cast(_acc80) / + static_cast(_results.size())) * + 100 + << " %" + << "\n"; + } + + (*os) << std::endl; +} + +// _____________________________________________________________________________ +std::pair Collector::getDa(const std::vector& a, + const std::vector& b) { + assert(a.size() == b.size()); + std::pair ret{0, 0}; + + // euclidean distance on web mercator is in meters on equator, + // and proportional to cos(lat) in both y directions + double fac = + cos(2 * atan(exp((a.front().front().getY() + a.back().back().getY()) / + 6378137.0)) - + 1.5707965); + + for (size_t i = 0; i < a.size(); i++) { + double fd = util::geo::frechetDist(a[i], b[i], 3 / fac) * fac; + if (fd >= 20) { + ret.first++; + ret.second += util::geo::len(a[i]) * fac; + } + } + + return ret; +} diff --git a/src/pfaedle/eval/Collector.h b/src/shapevl/Collector.h similarity index 64% rename from src/pfaedle/eval/Collector.h rename to src/shapevl/Collector.h index 853314e..876ccbf 100644 --- a/src/pfaedle/eval/Collector.h +++ b/src/shapevl/Collector.h @@ -5,6 +5,7 @@ #ifndef PFAEDLE_EVAL_COLLECTOR_H_ #define PFAEDLE_EVAL_COLLECTOR_H_ +#include #include #include #include @@ -12,13 +13,12 @@ #include #include #include "ad/cppgtfs/gtfs/Feed.h" -#include "pfaedle/gtfs/Feed.h" #include "pfaedle/Def.h" -#include "pfaedle/eval/Result.h" +#include "shapevl/Result.h" #include "util/geo/Geo.h" -using pfaedle::gtfs::Trip; using ad::cppgtfs::gtfs::Shape; +using ad::cppgtfs::gtfs::Trip; namespace pfaedle { namespace eval { @@ -28,18 +28,23 @@ namespace eval { */ class Collector { public: - Collector(const std::string& evalOutPath, const std::vector& dfBins) - : _noOrigShp(0), + Collector(std::ostream* reportOut) + : _trips(0), + _noOrigShp(0), _fdSum(0), _unmatchedSegSum(0), _unmatchedSegLengthSum(0), - _evalOutPath(evalOutPath), - _dfBins(dfBins) {} + _acc0(0), + _acc10(0), + _acc20(0), + _acc40(0), + _acc80(0), + _reportOut(reportOut) {} // Add a shape found by our tool newS for a trip t with newly calculated // station dist values with the old shape oldS - double add(const Trip* t, const Shape* oldS, const Shape& newS, - const std::vector& newDists); + double add(const Trip* oldT, const Shape* oldS, const Trip* newT, + const Shape* newS); // Return the set of all Result objects const std::set& getResults() const; @@ -47,44 +52,44 @@ class Collector { // Print general stats to os void printStats(std::ostream* os) const; - // Print histogramgs for the results to os - void printHisto(std::ostream* os, const std::set& result, - const std::vector& bins) const; + // Print general stats to os + void printShortStats(std::ostream* os) const; // Print a CSV for the results to os - void printCsv(std::ostream* os, const std::set& result, - const std::vector& bins) const; + void printCsv(std::ostream* os, const std::set& result) const; // Return the averaged average frechet distance double getAvgDist() const; - static LINE getWebMercLine(const Shape* s, double from, double to); - static LINE getWebMercLine(const Shape* s, double from, double to, - std::vector* dists); + static LINE getWebMercLine(const Shape* s, std::vector* dists); + + double getAcc() const; private: std::set _results; - std::set _resultsAN; - std::set _resultsAL; std::map > _dCache; std::map > > _dACache; + size_t _trips; size_t _noOrigShp; double _fdSum; size_t _unmatchedSegSum; double _unmatchedSegLengthSum; - std::string _evalOutPath; + size_t _acc0; + size_t _acc10; + size_t _acc20; + size_t _acc40; + size_t _acc80; - std::vector _dfBins; + std::ostream* _reportOut; static std::pair getDa(const std::vector& a, const std::vector& b); static std::vector segmentize(const Trip* t, const LINE& shape, - const std::vector& dists, - const std::vector* newTripDists); + const std::vector& dists); static std::vector getBins(double mind, double maxd, size_t steps); }; diff --git a/src/pfaedle/eval/Result.h b/src/shapevl/Result.h similarity index 93% rename from src/pfaedle/eval/Result.h rename to src/shapevl/Result.h index b039234..0bfa667 100644 --- a/src/pfaedle/eval/Result.h +++ b/src/shapevl/Result.h @@ -5,10 +5,9 @@ #ifndef PFAEDLE_EVAL_RESULT_H_ #define PFAEDLE_EVAL_RESULT_H_ -#include "pfaedle/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Feed.h" -using pfaedle::gtfs::Trip; +using ad::cppgtfs::gtfs::Trip; using ad::cppgtfs::gtfs::Shape; namespace pfaedle { diff --git a/src/shapevl/ShapevlMain.cpp b/src/shapevl/ShapevlMain.cpp new file mode 100644 index 0000000..3b94342 --- /dev/null +++ b/src/shapevl/ShapevlMain.cpp @@ -0,0 +1,174 @@ +// Copyright 2020, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#include +#include +#include +#include +#include +#include +#include +#include "ad/cppgtfs/Parser.h" +#include "shapevl/Collector.h" +#include "util/Misc.h" +#include "util/log/Log.h" + +std::atomic count(0); + +// _____________________________________________________________________________ +void printHelp(int argc, char** argv) { + UNUSED(argc); + std::cout << "Usage: " << argv[0] + << " [-f ] -g [-s] " + << "\n"; + std::cout + << "\nAllowed arguments:\n -g Ground truth GTFS file\n"; + std::cout << " -s Only output summary\n"; + std::cout << " -f Output full reports (per feed) to \n"; + std::cout + << " -m MOTs to match (GTFS MOT or string, default: all)\n"; +} + +// _____________________________________________________________________________ +void eval(const std::vector* paths, + std::vector* colls, + const std::set* mots, + const ad::cppgtfs::gtfs::Feed* evalFeed) { + while (1) { + int myFeed = count-- - 1; + if (myFeed < 0) return; + std::string path = (*paths)[myFeed]; + LOG(DEBUG) << "Reading eval feed " << path << " " + << " ..."; + ad::cppgtfs::gtfs::Feed feed; + ad::cppgtfs::Parser p; + + try { + p.parse(&feed, path); + } catch (const ad::cppgtfs::ParserException& ex) { + LOG(ERROR) << "Could not parse GTFS feed " << path << ", reason was:"; + std::cerr << ex.what() << std::endl; + exit(1); + } + + LOG(DEBUG) << "Evaluating " << path << "..."; + for (const auto oldTrip : evalFeed->getTrips()) { + if (!mots->count(oldTrip.second->getRoute()->getType())) continue; + auto newTrip = feed.getTrips().get(oldTrip.first); + if (!newTrip) + LOG(ERROR) << "Trip #" << oldTrip.first << " not present in " << path; + (*colls)[myFeed].add(oldTrip.second, oldTrip.second->getShape(), newTrip, + newTrip->getShape()); + } + } +} + +// _____________________________________________________________________________ +int main(int argc, char** argv) { + // disable output buffering for standard output + setbuf(stdout, NULL); + + // initialize randomness + srand(time(NULL) + rand()); // NOLINT + + std::string groundTruthFeedPath, motStr; + motStr = "all"; + ad::cppgtfs::gtfs::Feed groundTruthFeed; + std::string fullReportPath = ""; + std::vector evlFeedPaths; + std::set evlFeedPathsUniq; + std::vector evalColls; + std::vector reportStreams; + bool summarize = false; + + for (int i = 1; i < argc; i++) { + std::string cur = argv[i]; + if (cur == "-h" || cur == "--help") { + printHelp(argc, argv); + exit(0); + } else if (cur == "-g") { + if (++i >= argc) { + LOG(ERROR) << "Missing argument for ground truth (-g)."; + exit(1); + } + groundTruthFeedPath = argv[i]; + } else if (cur == "-s") { + summarize = true; + } else if (cur == "-f") { + if (++i >= argc) { + LOG(ERROR) << "Missing argument for full reports (-f)."; + exit(1); + } + fullReportPath = argv[i]; + } else if (cur == "-m") { + if (++i >= argc) { + LOG(ERROR) << "Missing argument for mot (-m)."; + exit(1); + } + motStr = argv[i]; + } else { + char fullPath[PATH_MAX + 1]; + if (!realpath(cur.c_str(), fullPath)) { + LOG(ERROR) << "Error while reading " << fullPath; + exit(1); + } + evlFeedPathsUniq.insert(fullPath); + } + } + + for (const auto& feedPath : evlFeedPathsUniq) { + evlFeedPaths.push_back(feedPath); + if (fullReportPath.size()) { + reportStreams.emplace_back(); + reportStreams.back().open(fullReportPath + "/" + + util::split(feedPath, '/').back() + + ".fullreport.tsv"); + evalColls.push_back({&reportStreams.back()}); + } else { + evalColls.push_back({0}); + } + count++; + } + + if (groundTruthFeedPath.size() == 0) { + LOG(ERROR) << "No ground truth feed path given (-g)."; + exit(1); + } + + std::set mots = + ad::cppgtfs::gtfs::flat::Route::getTypesFromString(util::trim(motStr)); + + std::vector evlFeeds(evlFeedPaths.size()); + + try { + LOG(DEBUG) << "Reading ground truth feed" << groundTruthFeedPath << " ..."; + ad::cppgtfs::Parser p; + p.parse(&groundTruthFeed, groundTruthFeedPath); + } catch (const ad::cppgtfs::ParserException& ex) { + LOG(ERROR) << "Could not parse input GTFS feed, reason was:"; + std::cerr << ex.what() << std::endl; + exit(1); + } + + size_t THREADS = std::thread::hardware_concurrency(); + + std::vector thrds(THREADS); + for (auto& thr : thrds) + thr = + std::thread(&eval, &evlFeedPaths, &evalColls, &mots, &groundTruthFeed); + + for (auto& thr : thrds) thr.join(); + + for (size_t i = 0; i < evalColls.size(); i++) { + if (summarize) { + std::cout << evlFeedPaths[i] << ": "; + evalColls[i].printShortStats(&std::cout); + std::cout << std::endl; + } else { + std::cout << " == Evaluation results for " << evlFeedPaths[i] + << " ===" << std::endl; + evalColls[i].printStats(&std::cout); + } + } +} diff --git a/src/util/3rdparty/MurmurHash3.cpp b/src/util/3rdparty/MurmurHash3.cpp new file mode 100644 index 0000000..aa7982d --- /dev/null +++ b/src/util/3rdparty/MurmurHash3.cpp @@ -0,0 +1,335 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - The x86 and x64 versions do _not_ produce the same results, as the +// algorithms are optimized for their respective platforms. You can still +// compile and run any of them on any platform, but your performance with the +// non-native version will be less than optimal. + +#include "MurmurHash3.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) + +#define FORCE_INLINE __forceinline + +#include + +#define ROTL32(x,y) _rotl(x,y) +#define ROTL64(x,y) _rotl64(x,y) + +#define BIG_CONSTANT(x) (x) + +// Other compilers + +#else // defined(_MSC_VER) + +#define FORCE_INLINE inline __attribute__((always_inline)) + +inline uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +inline uint64_t rotl64 ( uint64_t x, int8_t r ) +{ + return (x << r) | (x >> (64 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) +{ + return p[i]; +} + +FORCE_INLINE uint64_t getblock64 ( const uint64_t * p, int i ) +{ + return p[i]; +} + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +FORCE_INLINE uint32_t fmix32 ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +FORCE_INLINE uint64_t fmix64 ( uint64_t k ) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 4; + + uint32_t h1 = seed; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = getblock32(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 = fmix32(h1); + + *(uint32_t*)out = h1; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_128 ( const void * key, const int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + + uint32_t h1 = seed; + uint32_t h2 = seed; + uint32_t h3 = seed; + uint32_t h4 = seed; + + const uint32_t c1 = 0x239b961b; + const uint32_t c2 = 0xab0e9789; + const uint32_t c3 = 0x38b34ae5; + const uint32_t c4 = 0xa1e38b93; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = getblock32(blocks,i*4+0); + uint32_t k2 = getblock32(blocks,i*4+1); + uint32_t k3 = getblock32(blocks,i*4+2); + uint32_t k4 = getblock32(blocks,i*4+3); + + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + + h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; + + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; + + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; + + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint32_t k1 = 0; + uint32_t k2 = 0; + uint32_t k3 = 0; + uint32_t k4 = 0; + + switch(len & 15) + { + case 15: k4 ^= tail[14] << 16; + case 14: k4 ^= tail[13] << 8; + case 13: k4 ^= tail[12] << 0; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + case 12: k3 ^= tail[11] << 24; + case 11: k3 ^= tail[10] << 16; + case 10: k3 ^= tail[ 9] << 8; + case 9: k3 ^= tail[ 8] << 0; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + case 8: k2 ^= tail[ 7] << 24; + case 7: k2 ^= tail[ 6] << 16; + case 6: k2 ^= tail[ 5] << 8; + case 5: k2 ^= tail[ 4] << 0; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + case 4: k1 ^= tail[ 3] << 24; + case 3: k1 ^= tail[ 2] << 16; + case 2: k1 ^= tail[ 1] << 8; + case 1: k1 ^= tail[ 0] << 0; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + h1 = fmix32(h1); + h2 = fmix32(h2); + h3 = fmix32(h3); + h4 = fmix32(h4); + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + ((uint32_t*)out)[0] = h1; + ((uint32_t*)out)[1] = h2; + ((uint32_t*)out)[2] = h3; + ((uint32_t*)out)[3] = h4; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x64_128 ( const void * key, const int len, + const uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + + uint64_t h1 = seed; + uint64_t h2 = seed; + + const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); + const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); + + //---------- + // body + + const uint64_t * blocks = (const uint64_t *)(data); + + for(int i = 0; i < nblocks; i++) + { + uint64_t k1 = getblock64(blocks,i*2+0); + uint64_t k2 = getblock64(blocks,i*2+1); + + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint64_t k1 = 0; + uint64_t k2 = 0; + + switch(len & 15) + { + case 15: k2 ^= ((uint64_t)tail[14]) << 48; + case 14: k2 ^= ((uint64_t)tail[13]) << 40; + case 13: k2 ^= ((uint64_t)tail[12]) << 32; + case 12: k2 ^= ((uint64_t)tail[11]) << 24; + case 11: k2 ^= ((uint64_t)tail[10]) << 16; + case 10: k2 ^= ((uint64_t)tail[ 9]) << 8; + case 9: k2 ^= ((uint64_t)tail[ 8]) << 0; + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + case 8: k1 ^= ((uint64_t)tail[ 7]) << 56; + case 7: k1 ^= ((uint64_t)tail[ 6]) << 48; + case 6: k1 ^= ((uint64_t)tail[ 5]) << 40; + case 5: k1 ^= ((uint64_t)tail[ 4]) << 32; + case 4: k1 ^= ((uint64_t)tail[ 3]) << 24; + case 3: k1 ^= ((uint64_t)tail[ 2]) << 16; + case 2: k1 ^= ((uint64_t)tail[ 1]) << 8; + case 1: k1 ^= ((uint64_t)tail[ 0]) << 0; + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + ((uint64_t*)out)[0] = h1; + ((uint64_t*)out)[1] = h2; +} + +//----------------------------------------------------------------------------- + diff --git a/src/util/3rdparty/MurmurHash3.h b/src/util/3rdparty/MurmurHash3.h new file mode 100644 index 0000000..e1c6d34 --- /dev/null +++ b/src/util/3rdparty/MurmurHash3.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +#ifndef _MURMURHASH3_H_ +#define _MURMURHASH3_H_ + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) && (_MSC_VER < 1600) + +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; + +// Other compilers + +#else // defined(_MSC_VER) + +#include + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ); + +void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out ); + +void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out ); + +//----------------------------------------------------------------------------- + +#endif // _MURMURHASH3_H_ diff --git a/src/util/3rdparty/dtoa_milo.h b/src/util/3rdparty/dtoa_milo.h new file mode 100644 index 0000000..1ed2670 --- /dev/null +++ b/src/util/3rdparty/dtoa_milo.h @@ -0,0 +1,454 @@ +// based on +// https://github.com/miloyip/dtoa-benchmark/blob/master/src/milo/dtoa_milo.h + +#pragma once +#include +#include +#include + +#if defined(_MSC_VER) +#include +#include "msinttypes/stdint.h" +#else +#include +#endif + +namespace gcc_ints { +__extension__ typedef __int128 int128; +__extension__ typedef unsigned __int128 uint128; +} // namespace gcc_ints + +#define UINT64_C2(h, l) \ + ((static_cast(h) << 32) | static_cast(l)) + +namespace util { + +struct DiyFp { + DiyFp() {} + + DiyFp(uint64_t f, int e) : f(f), e(e) {} + + DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = {d}; + + int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + assert(e == rhs.e); + assert(f >= rhs.f); + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && \ + defined(__x86_64__) + gcc_ints::uint128 p = static_cast(f) * + static_cast(rhs.f); + uint64_t h = p >> 64; + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & kDpHiddenBit)) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 1); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1); + return res; +#endif + } + + DiyFp NormalizeBoundary() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#else + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; +#endif + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) + : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMinExponent = -kDpExponentBias; + static const uint64_t kDpExponentMask = UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPower(int e, int* K) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + UINT64_C2(0xfa8fd5a0, 0x081c0288), UINT64_C2(0xbaaee17f, 0xa23ebf76), + UINT64_C2(0x8b16fb20, 0x3055ac76), UINT64_C2(0xcf42894a, 0x5dce35ea), + UINT64_C2(0x9a6bb0aa, 0x55653b2d), UINT64_C2(0xe61acf03, 0x3d1a45df), + UINT64_C2(0xab70fe17, 0xc79ac6ca), UINT64_C2(0xff77b1fc, 0xbebcdc4f), + UINT64_C2(0xbe5691ef, 0x416bd60c), UINT64_C2(0x8dd01fad, 0x907ffc3c), + UINT64_C2(0xd3515c28, 0x31559a83), UINT64_C2(0x9d71ac8f, 0xada6c9b5), + UINT64_C2(0xea9c2277, 0x23ee8bcb), UINT64_C2(0xaecc4991, 0x4078536d), + UINT64_C2(0x823c1279, 0x5db6ce57), UINT64_C2(0xc2109436, 0x4dfb5637), + UINT64_C2(0x9096ea6f, 0x3848984f), UINT64_C2(0xd77485cb, 0x25823ac7), + UINT64_C2(0xa086cfcd, 0x97bf97f4), UINT64_C2(0xef340a98, 0x172aace5), + UINT64_C2(0xb23867fb, 0x2a35b28e), UINT64_C2(0x84c8d4df, 0xd2c63f3b), + UINT64_C2(0xc5dd4427, 0x1ad3cdba), UINT64_C2(0x936b9fce, 0xbb25c996), + UINT64_C2(0xdbac6c24, 0x7d62a584), UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + UINT64_C2(0xf3e2f893, 0xdec3f126), UINT64_C2(0xb5b5ada8, 0xaaff80b8), + UINT64_C2(0x87625f05, 0x6c7c4a8b), UINT64_C2(0xc9bcff60, 0x34c13053), + UINT64_C2(0x964e858c, 0x91ba2655), UINT64_C2(0xdff97724, 0x70297ebd), + UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), UINT64_C2(0xf8a95fcf, 0x88747d94), + UINT64_C2(0xb9447093, 0x8fa89bcf), UINT64_C2(0x8a08f0f8, 0xbf0f156b), + UINT64_C2(0xcdb02555, 0x653131b6), UINT64_C2(0x993fe2c6, 0xd07b7fac), + UINT64_C2(0xe45c10c4, 0x2a2b3b06), UINT64_C2(0xaa242499, 0x697392d3), + UINT64_C2(0xfd87b5f2, 0x8300ca0e), UINT64_C2(0xbce50864, 0x92111aeb), + UINT64_C2(0x8cbccc09, 0x6f5088cc), UINT64_C2(0xd1b71758, 0xe219652c), + UINT64_C2(0x9c400000, 0x00000000), UINT64_C2(0xe8d4a510, 0x00000000), + UINT64_C2(0xad78ebc5, 0xac620000), UINT64_C2(0x813f3978, 0xf8940984), + UINT64_C2(0xc097ce7b, 0xc90715b3), UINT64_C2(0x8f7e32ce, 0x7bea5c70), + UINT64_C2(0xd5d238a4, 0xabe98068), UINT64_C2(0x9f4f2726, 0x179a2245), + UINT64_C2(0xed63a231, 0xd4c4fb27), UINT64_C2(0xb0de6538, 0x8cc8ada8), + UINT64_C2(0x83c7088e, 0x1aab65db), UINT64_C2(0xc45d1df9, 0x42711d9a), + UINT64_C2(0x924d692c, 0xa61be758), UINT64_C2(0xda01ee64, 0x1a708dea), + UINT64_C2(0xa26da399, 0x9aef774a), UINT64_C2(0xf209787b, 0xb47d6b85), + UINT64_C2(0xb454e4a1, 0x79dd1877), UINT64_C2(0x865b8692, 0x5b9bc5c2), + UINT64_C2(0xc83553c5, 0xc8965d3d), UINT64_C2(0x952ab45c, 0xfa97a0b3), + UINT64_C2(0xde469fbd, 0x99a05fe3), UINT64_C2(0xa59bc234, 0xdb398c25), + UINT64_C2(0xf6c69a72, 0xa3989f5c), UINT64_C2(0xb7dcbf53, 0x54e9bece), + UINT64_C2(0x88fcf317, 0xf22241e2), UINT64_C2(0xcc20ce9b, 0xd35c78a5), + UINT64_C2(0x98165af3, 0x7b2153df), UINT64_C2(0xe2a0b5dc, 0x971f303a), + UINT64_C2(0xa8d9d153, 0x5ce3b396), UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + UINT64_C2(0xbb764c4c, 0xa7a44410), UINT64_C2(0x8bab8eef, 0xb6409c1a), + UINT64_C2(0xd01fef10, 0xa657842c), UINT64_C2(0x9b10a4e5, 0xe9913129), + UINT64_C2(0xe7109bfb, 0xa19c0c9d), UINT64_C2(0xac2820d9, 0x623bf429), + UINT64_C2(0x80444b5e, 0x7aa7cf85), UINT64_C2(0xbf21e440, 0x03acdd2d), + UINT64_C2(0x8e679c2f, 0x5e44ff8f), UINT64_C2(0xd433179d, 0x9c8cb841), + UINT64_C2(0x9e19db92, 0xb4e31ba9), UINT64_C2(0xeb96bf6e, 0xbadf77d9), + UINT64_C2(0xaf87023b, 0x9bf0ee6b)}; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; + + // int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast( + index << 3)); // decimal exponent no need lookup table + + assert(index < sizeof(kCachedPowers_F) / sizeof(kCachedPowers_F[0])); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, + uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in + // this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + return 10; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, + char* buffer, int* len, int* K) { + static const uint64_t kPow10[] = {1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000}; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = static_cast(CountDecimalDigit32(p1)); + *len = 0; + + while (kappa > 0) { + uint32_t d; + switch (kappa) { + case 10: + d = p1 / 1000000000; + p1 %= 1000000000; + break; + case 9: + d = p1 / 100000000; + p1 %= 100000000; + break; + case 8: + d = p1 / 10000000; + p1 %= 10000000; + break; + case 7: + d = p1 / 1000000; + p1 %= 1000000; + break; + case 6: + d = p1 / 100000; + p1 %= 100000; + break; + case 5: + d = p1 / 10000; + p1 %= 10000; + break; + case 4: + d = p1 / 1000; + p1 %= 1000; + break; + case 3: + d = p1 / 100; + p1 %= 100; + break; + case 2: + d = p1 / 10; + p1 %= 10; + break; + case 1: + d = p1; + p1 = 0; + break; + default: +#if defined(_MSC_VER) + __assume(0); +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + __builtin_unreachable(); +#else + d = 0; +#endif + } + if (d || *len) buffer[(*len)++] = '0' + static_cast(d); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, + static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) buffer[(*len)++] = '0' + d; + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', + '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', + '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', + '7', '3', '8', '3', '9', '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', + '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', '5', '1', '5', + '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', + '7', '6', '8', '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', + '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', + '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', + '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', + '7', '9', '8', '9', '9'}; + return cDigitsLut; +} + +inline void WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = '0' + static_cast(K / 100); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } else + *buffer++ = '0' + static_cast(K); + + *buffer = '\0'; +} + +inline void Prettify(char* buffer, int length, int k) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (length <= kk && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + buffer[kk + 2] = '\0'; + } else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + memmove(&buffer[kk + 1], &buffer[kk], length - kk); + buffer[kk] = '.'; + buffer[length + 1] = '\0'; + } else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + memmove(&buffer[offset], &buffer[0], length); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) buffer[i] = '0'; + buffer[length + offset] = '\0'; + } else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + WriteExponent(kk - 1, &buffer[2]); + } else { + // 1234e30 -> 1.234e33 + memmove(&buffer[2], &buffer[1], length - 1); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline void dtoa_milo(double value, char* buffer) { + // Not handling NaN and inf + assert(!std::isnan(value)); + assert(!std::isinf(value)); + + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + buffer[3] = '\0'; + } else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + Prettify(buffer, length, K); + } +} +} // namespace util diff --git a/src/util/Misc.h b/src/util/Misc.h index 6bac94b..feb3207 100644 --- a/src/util/Misc.h +++ b/src/util/Misc.h @@ -9,18 +9,81 @@ #include #include #include +#include +#include +#include #include #include #include +#include +#include +#include "3rdparty/dtoa_milo.h" #define UNUSED(expr) do { (void)(expr); } while (0) #define TIME() std::chrono::high_resolution_clock::now() -#define TOOK(t1, t2) (std::chrono::duration_cast(t2 - t1).count() / 1000.0) +#define TOOK(t1, t2) (std::chrono::duration_cast(t2 - t1).count() / 1000.0) #define T_START(n) auto _tstart_##n = std::chrono::high_resolution_clock::now() -#define T_STOP(n) (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0) +#define T_STOP(n) (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0) + +#define _TEST3(s, o, e) if (!(s o e)) { std::cerr << "\n" << __FILE__ << ":" << __LINE__ << ": Test failed!\n Expected " << #s << " " << #o " " << (e) << ", got " << (s) << std::endl; exit(1);} +#define _TEST2(s, e) _TEST3(s, ==, o) +#define _TEST1(s) _TEST3(static_cast(s), ==, true) + +#define _GET_TEST_MACRO(_1,_2,_3,NAME,...) NAME +#define TEST(...) _GET_TEST_MACRO(__VA_ARGS__, _TEST3, _TEST2, _TEST1, UNUSED)(__VA_ARGS__) + +#define TODO(msg) std::cerr << "\n" __FILE__ << ":" << __LINE__ << ": TODO: " << #msg << std::endl; + +#if defined(_WIN32) +#include +#include +#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) +#include +#include +#if defined(__APPLE__) && defined(__MACH__) +#include +#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) +#include +#include +#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) +#include +#endif +#else +#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." +#endif namespace util { +struct hashPair { + template + size_t operator()(const std::pair& p) const { + auto h1 = std::hash{}(p.first); + auto h2 = std::hash{}(p.second); + return h1 ^ h2; + } +}; + +template +class SparseMatrix { + public: + Val get(const Key& x, const Key& y) const { + auto a = _m.find(std::pair(x, y)); + if (a == _m.end()) return Def; + return a->second; + } + + void set(Key x, Key y, Val v) { + _m[std::pair(x, y)] = v; + } + + const std::map, Val>& vals() const { + return _m; + } + + private: + std::map, Val> _m; +}; + // cached first 10 powers of 10 static int pow10[10] = { 1, 10, 100, 1000, 10000, @@ -28,7 +91,7 @@ static int pow10[10] = { // _____________________________________________________________________________ inline uint64_t factorial(uint64_t n) { - if (n == 1) return n; + if (n < 2) return 1; return n * factorial(n - 1); } @@ -89,6 +152,82 @@ inline double atof(const char* p, uint8_t mn) { // _____________________________________________________________________________ inline double atof(const char* p) { return atof(p, 38); } +// _____________________________________________________________________________ +template +int merge(V* lst, V* tmpLst, size_t l, size_t m, size_t r) { + size_t ret = 0; + + size_t lp = l; + size_t rp = m; + size_t outp = l; + + while (lp < m && rp < r + 1) { + if (lst[lp] <= lst[rp]) { + // if left element is smaller or equal, add it to return list, + // increase left pointer + tmpLst[outp] = lst[lp]; + lp++; + } else { + // if left element is bigger, add the right element, add it to ret, + // increase right pointer + tmpLst[outp] = lst[rp]; + rp++; + + // if the left element was bigger, everything to the right in the + // left list is also bigger, and all these m - i elements were + // initially in the wrong order! Count these inversions. + ret += m - lp; + } + + outp++; + } + + // fill in remaining values + if (lp < m) std::memcpy(tmpLst + outp, lst + lp, (m - lp) * sizeof(V)); + if (rp <= r) std::memcpy(tmpLst + outp, lst + rp, ((r + 1) - rp) * sizeof(V)); + + // copy to output + std::memcpy(lst + l, tmpLst + l, ((r + 1) - l) * sizeof(V)); + + return ret; +} + +// _____________________________________________________________________________ +template +size_t mergeInvCount(V* lst, V* tmpLst, size_t l, size_t r) { + size_t ret = 0; + if (l < r) { + size_t m = (r + l) / 2; + + ret += mergeInvCount(lst, tmpLst, l, m); + ret += mergeInvCount(lst, tmpLst, m + 1, r); + + ret += merge(lst, tmpLst, l, m + 1, r); + } + return ret; +} + +// _____________________________________________________________________________ +template +size_t inversions(const std::vector& v) { + if (v.size() < 2) return 0; // no inversions possible + + // unroll some simple cases + if (v.size() == 2) return v[1] < v[0]; + if (v.size() == 3) return (v[0] > v[1]) + (v[0] > v[2]) + (v[1] > v[2]); + + auto tmpLst = new V[v.size()]; + auto lst = new V[v.size()]; + + for (size_t i = 0; i < v.size(); i++) lst[i] = v[i]; + + size_t ret = mergeInvCount(lst, tmpLst, 0, v.size() - 1); + delete[] tmpLst; + delete[] lst; + return ret; +} + + // _____________________________________________________________________________ inline std::string getHomeDir() { // parse implicit paths @@ -115,6 +254,29 @@ inline std::string getHomeDir() { return ret; } +// _____________________________________________________________________________ +inline char* readableSize(double size, char* buf) { + int i = 0; + const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB"}; + while (size > 1024 && i < 5) { + size /= 1024; + i++; + } + sprintf(buf, "%.*f %s", i, size, units[i]); + return buf; +} + +// _____________________________________________________________________________ +inline std::string readableSize(double size) { + char buffer[30]; + return readableSize(size, buffer); +} + +// _____________________________________________________________________________ +inline float f_rsqrt(float x) { + return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); +} + // _____________________________________________________________________________ inline std::string getTmpDir() { // first, check if an env variable is set @@ -131,6 +293,150 @@ inline std::string getTmpDir() { return getHomeDir(); } +// _____________________________________________________________________________ +class approx { + public: + explicit approx(double magnitude) + : _epsilon{std::numeric_limits::epsilon() * 100}, + _magnitude{magnitude} {} + + friend bool operator==(double lhs, approx const& rhs) { + return std::abs(lhs - rhs._magnitude) < rhs._epsilon; + } + + friend bool operator==(approx const& lhs, double rhs) { + return operator==(rhs, lhs); + } + + friend bool operator!=(double lhs, approx const& rhs) { + return !operator==(lhs, rhs); + } + + friend bool operator!=(approx const& lhs, double rhs) { + return !operator==(rhs, lhs); + } + + friend bool operator<=(double lhs, approx const& rhs) { + return lhs < rhs._magnitude || lhs == rhs; + } + + friend bool operator<=(approx const& lhs, double rhs) { + return lhs._magnitude < rhs || lhs == rhs; + } + + friend bool operator>=(double lhs, approx const& rhs) { + return lhs > rhs._magnitude || lhs == rhs; + } + + friend bool operator>=(approx const& lhs, double rhs) { + return lhs._magnitude > rhs || lhs == rhs; + } + + friend std::ostream& operator<< (std::ostream &out, const approx &a) { + out << "~" << a._magnitude; + return out; + } + + private: + double _epsilon; + double _magnitude; +}; + +/* + * Author: David Robert Nadeau + * Site: http://NadeauSoftware.com/ + * License: Creative Commons Attribution 3.0 Unported License + * http://creativecommons.org/licenses/by/3.0/deed.en_US + */ + +/** + * Returns the peak (maximum so far) resident set size (physical + * memory use) measured in bytes, or zero if the value cannot be + * determined on this OS. + */ + +// _____________________________________________________________________________ +inline size_t getPeakRSS() { +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + return (size_t)info.PeakWorkingSetSize; + +#elif (defined(_AIX) || defined(__TOS__AIX__)) || \ + (defined(__sun__) || defined(__sun) || \ + defined(sun) && (defined(__SVR4) || defined(__svr4__))) + /* AIX and Solaris ------------------------------------------ */ + struct psinfo psinfo; + int fd = -1; + if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) + return (size_t)0L; /* Can't open? */ + if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { + close(fd); + return (size_t)0L; /* Can't read? */ + } + close(fd); + return (size_t)(psinfo.pr_rssize * 1024L); + +#elif defined(__unix__) || defined(__unix) || defined(unix) || \ + (defined(__APPLE__) && defined(__MACH__)) + /* BSD, Linux, and OSX -------------------------------------- */ + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); +#if defined(__APPLE__) && defined(__MACH__) + return (size_t)rusage.ru_maxrss; +#else + return (size_t)(rusage.ru_maxrss * 1024L); +#endif + +#else + /* Unknown OS ----------------------------------------------- */ + return (size_t)0L; /* Unsupported. */ +#endif +} + +/* + * Returns the current resident set size (physical memory use) measured + * in bytes, or zero if the value cannot be determined on this OS. + */ + +// _____________________________________________________________________________ +inline size_t getCurrentRSS() { +#if defined(_WIN32) + /* Windows -------------------------------------------------- */ + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + return (size_t)info.WorkingSetSize; + +#elif defined(__APPLE__) && defined(__MACH__) + /* OSX ------------------------------------------------------ */ + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, + &infoCount) != KERN_SUCCESS) + return (size_t)0L; /* Can't access? */ + return (size_t)info.resident_size; + +#elif defined(__linux__) || defined(__linux) || defined(linux) || \ + defined(__gnu_linux__) + /* Linux ---------------------------------------------------- */ + long rss = 0L; + FILE* fp = NULL; + if ((fp = fopen("/proc/self/statm", "r")) == NULL) + return (size_t)0L; /* Can't open? */ + if (fscanf(fp, "%*s%ld", &rss) != 1) { + fclose(fp); + return (size_t)0L; /* Can't read? */ + } + fclose(fp); + return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE); + +#else + /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ + return (size_t)0L; /* Unsupported. */ +#endif +} + } // namespace util #endif // UTIL_MISC_H_ diff --git a/src/util/PriorityQueue.h b/src/util/PriorityQueue.h new file mode 100644 index 0000000..13bd973 --- /dev/null +++ b/src/util/PriorityQueue.h @@ -0,0 +1,39 @@ +// Copyright 2019, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef UTIL_PRIORITYQUEUE_H_ +#define UTIL_PRIORITYQUEUE_H_ + +#include +#include +#include + +namespace util { +template +class PriorityQueue { + struct _ByFirst { + bool operator()(const std::pair& a, const std::pair& b) { + return a.first > b.first; + } + }; + + + public: + PriorityQueue() : _last(std::numeric_limits::lowest()) {} + void push(K k, const V& v); + const K topKey() ; + const V& topVal() ; + void pop(); + + bool empty() const; + + private: + K _last; + std::priority_queue, std::vector>, _ByFirst> + _pq; +}; +#include "util/PriorityQueue.tpp" +} // namespace util + +#endif diff --git a/src/util/PriorityQueue.tpp b/src/util/PriorityQueue.tpp new file mode 100644 index 0000000..a46adbb --- /dev/null +++ b/src/util/PriorityQueue.tpp @@ -0,0 +1,41 @@ +// Copyright 2019, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +// _____________________________________________________________________________ +template +void PriorityQueue::push(K key, const V& val) { + // if (_last - key > 0) key = _last; + if (key < _last) { + std::cout << std::setprecision(10) << _last << " vs " << key << std::endl; + key = _last; + } + // assert(key >= _last); + _pq.emplace(std::pair(key, val)); +} + +// _____________________________________________________________________________ +template +const K PriorityQueue::topKey() { + _last = _pq.top().first; + return _pq.top().first; +} + +// _____________________________________________________________________________ +template +const V& PriorityQueue::topVal() { + _last = _pq.top().first; + return _pq.top().second; +} + +// _____________________________________________________________________________ +template +bool PriorityQueue::empty() const { + return _pq.empty(); +} + +// _____________________________________________________________________________ +template +void PriorityQueue::pop() { + _pq.pop(); +} diff --git a/src/util/String.h b/src/util/String.h index 536b713..379785f 100644 --- a/src/util/String.h +++ b/src/util/String.h @@ -6,11 +6,17 @@ #define UTIL_STRING_H_ #include +#include +#include #include +#include #include +#include +#include #include #include #include +#include namespace util { @@ -169,7 +175,8 @@ inline size_t editDist(const std::string& s1, const std::string& s2) { } // _____________________________________________________________________________ -inline size_t prefixEditDist(const std::string& prefix, const std::string& s, +template +inline size_t prefixEditDist(const String& prefix, const String& s, size_t deltaMax) { // https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++ size_t len1 = prefix.size(); @@ -200,10 +207,45 @@ inline size_t prefixEditDist(const std::string& prefix, const std::string& s, } // _____________________________________________________________________________ -inline size_t prefixEditDist(const std::string& prefix, const std::string& s) { +template +inline size_t prefixEditDist(const String& prefix, const String& s) { return prefixEditDist(prefix, s, s.size()); } +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const char* s) { + return prefixEditDist(std::string(prefix), std::string(s)); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const char* s, + size_t deltaMax) { + return prefixEditDist(std::string(prefix), std::string(s), + deltaMax); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const std::string& s) { + return prefixEditDist(std::string(prefix), s); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const char* prefix, const std::string& s, + size_t deltaMax) { + return prefixEditDist(std::string(prefix), s, deltaMax); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const std::string& prefix, const char* s) { + return prefixEditDist(prefix, std::string(s)); +} + +// _____________________________________________________________________________ +inline size_t prefixEditDist(const std::string& prefix, const char* s, + size_t deltaMax) { + return prefixEditDist(prefix, std::string(s), deltaMax); +} + // _____________________________________________________________________________ inline std::string toUpper(std::string str) { std::transform(str.begin(), str.end(), str.begin(), toupper); @@ -251,10 +293,66 @@ inline std::string normalizeWhiteSpace(const std::string& input) { } // _____________________________________________________________________________ -template +inline std::wstring toWStr(const std::string& str) { + std::wstring_convert> converter; + return converter.from_bytes(str); +} + +// _____________________________________________________________________________ +inline std::string toNStr(const std::wstring& wstr) { + std::wstring_convert> converter; + return converter.to_bytes(wstr); +} + +// _____________________________________________________________________________ +inline std::vector tokenize(const std::string& str) { + std::vector ret; + std::wstring wStr = toWStr(str); + + std::wstring cur; + for (size_t i = 0; i < wStr.size(); ++i) { + if (!std::iswalnum(wStr[i])) { + if (cur.size()) ret.push_back(toNStr(cur)); + cur = L""; + continue; + } + cur += wStr[i]; + } + if (cur.size()) ret.push_back(toNStr(cur)); + + return ret; +} + +// _____________________________________________________________________________ +inline double jaccardSimi(const std::string& a, + const std::string& b) { + std::set sa, sb; + + auto toksA = tokenize(a); + auto toksB = tokenize(b); + + // 0 if both are empty + if (toksA.size() == 0 && toksB.size() == 0) return 0; + + sa.insert(toksA.begin(), toksA.end()); + sb.insert(toksB.begin(), toksB.end()); + + std::set isect; + + std::set_intersection(sa.begin(), sa.end(), sb.begin(), sb.end(), + std::inserter(isect, isect.begin())); + + double sInter = isect.size(); + double s1 = sa.size(); + double s2 = sb.size(); + return sInter / (s1 + s2 - sInter); +} + +// _____________________________________________________________________________ +template inline std::string implode(const std::vector& vec, const char* del) { return implode(vec.begin(), vec.end(), del); } -} +} // namespace util #endif // UTIL_STRING_H_ diff --git a/src/util/geo/BezierCurve.tpp b/src/util/geo/BezierCurve.tpp index fb7e6ca..4f08a31 100644 --- a/src/util/geo/BezierCurve.tpp +++ b/src/util/geo/BezierCurve.tpp @@ -64,7 +64,7 @@ const PolyLine& BezierCurve::render(double d) { } // _____________________________________________________________________________ -double CubicPolynom::valueAt(double atx) const { +inline double CubicPolynom::valueAt(double atx) const { double dx = atx - x; return a + b * dx + c * dx * dx + d * dx * dx * dx; } diff --git a/src/util/geo/CircularSegment.h b/src/util/geo/CircularSegment.h new file mode 100644 index 0000000..b80e60d --- /dev/null +++ b/src/util/geo/CircularSegment.h @@ -0,0 +1,41 @@ +// Copyright 2016, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +#ifndef UTIL_GEO_CIRCULARSEGMENT_H_ +#define UTIL_GEO_CIRCULARSEGMENT_H_ + +#include +#include "util/geo/Geo.h" +#include "util/geo/PolyLine.h" + +namespace util { +namespace geo { + +/** + * Circular segment + */ +template +class CircularSegment { + public: + CircularSegment(const Point& a, double ang, const Point& c); + + const PolyLine& render(double d); + + private: + // store the rendered polyline for quicker access + PolyLine _rendered; + + const Point& _a, _c; + double _renderD; + + double _ang, _rad, _s, _initAng; + + Point valueAt(double t) const; +}; + +#include "util/geo/CircularSegment.tpp" +} +} + +#endif // UTIL_GEO_BEZIERCURVE_H_ diff --git a/src/util/geo/CircularSegment.tpp b/src/util/geo/CircularSegment.tpp new file mode 100644 index 0000000..6058c68 --- /dev/null +++ b/src/util/geo/CircularSegment.tpp @@ -0,0 +1,51 @@ +// Copyright 2016, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Patrick Brosi + +// _____________________________________________________________________________ +template +CircularSegment::CircularSegment(const Point& a, double ang, + const Point& c) : _a(a), _c(c), _renderD(0), _ang(ang) + { + + _rad = dist(a, c); + _s = fabs(_ang * _rad); + _initAng = angBetween(c, a); +} + +// _____________________________________________________________________________ +template +Point CircularSegment::valueAt(double ang) const { + double xPos = _c.getX() + _rad * cos(ang); + double yPos = _c.getY() + _rad * sin(ang); + return Point(xPos, yPos); +} + +// _____________________________________________________________________________ +template +const PolyLine& CircularSegment::render(double d) { + assert(d > 0); + if (fabs(d - _renderD) < 0.001) return _rendered; + _renderD = d; + + if (_s == 0) { + _rendered << _a << _a; + return _rendered; + } + + _rendered.empty(); + double n = _s / d, dt = 1 / n, t = 0; + + bool cancel = false; + while (true) { + _rendered << valueAt(_initAng + t * _ang); + t += dt; + if (cancel) break; + if (t > 1) { + t = 1; + cancel = true; + } + } + + return _rendered; +} diff --git a/src/util/geo/Geo.h b/src/util/geo/Geo.h index ffbfada..c11557c 100644 --- a/src/util/geo/Geo.h +++ b/src/util/geo/Geo.h @@ -51,6 +51,11 @@ typedef Polygon IPolygon; const static double EPSILON = 0.00001; const static double RAD = 0.017453292519943295; // PI/180 +const static double IRAD = 180.0 / M_PI; // 180 / PI +const static double AVERAGING_STEP = 20; + +const static double M_PER_DEG = 111319.4; + // _____________________________________________________________________________ template @@ -203,6 +208,14 @@ inline Polygon move(Polygon geo, double x, double y) { return geo; } +// _____________________________________________________________________________ +template +inline Box move(Box geo, double x, double y) { + geo.setLowerLeft(move(geo.getLowerLeft(), x, y)); + geo.setUpperRight(move(geo.getUpperRight(), x, y)); + return geo; +} + // _____________________________________________________________________________ template