* speed up hop-to-hop calculations
* better and faster trip clustering: trip tries * add --write-colors to extract line colors from OSM data * refactor config parameter names, update default pfaedle.cfg * add --stats for writing a stats.json file * add --no-fast-hops, --no-a-star, --no-trie for debugging * general refactoring
This commit is contained in:
parent
f1822868c5
commit
4c29892658
126 changed files with 14576 additions and 12196 deletions
|
@ -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
|
||||
)
|
||||
|
|
41
README.md
41
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 <GTFS TRIP ID>` only calculate shape for a single trip (specified via its GTFS trip id) and output it as GeoJSON to
|
||||
`<dbg-path>/path.json`
|
||||
* `--write-graph` write the graph used for routing as GeoJSON to
|
||||
`<dbg-path>/graph.json`
|
||||
* `--write-cgraph` if `-T` is set, write the combination graph used for
|
||||
routing as GeoJSON to `<dbg-path>/combgraph.json`
|
||||
* `--write-trgraph` write the complete network graph to `<dbg-path>/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): `<install prefix>/etc/pfaedle/pfaedle.cfg`, `$HOME/.config/pfaedle/pfaedle.cfg`, `<CWD>/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 <dataset>.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 <dataset>.eval` in `/eval`.
|
||||
|
||||
|
||||
## Evaluation requirements
|
||||
|
||||
* zlib
|
||||
|
||||
On Debianesque systems, type
|
||||
|
||||
```
|
||||
sudo apt-get install zlib1g-dev
|
||||
```
|
||||
|
||||
to install the dependencies.
|
||||
|
|
133
eval/Makefile
133
eval/Makefile
|
@ -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 $@
|
|
@ -1,987 +0,0 @@
|
|||
# Copyright 2018, University of Freiburg
|
||||
# Chair of Algorithms and Datastructures
|
||||
# Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
[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,}$ -> '';
|
1002
eval/eval.cfg
1002
eval/eval.cfg
File diff suppressed because it is too large
Load diff
7371
geo/pfaedle.qgs
7371
geo/pfaedle.qgs
File diff suppressed because it is too large
Load diff
514
pfaedle.cfg
514
pfaedle.cfg
|
@ -3,6 +3,10 @@
|
|||
# Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
[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
|
||||
|
|
|
@ -12,3 +12,4 @@ add_subdirectory(util)
|
|||
add_subdirectory(pfaedle)
|
||||
add_subdirectory(cppgtfs)
|
||||
add_subdirectory(configparser)
|
||||
add_subdirectory(shapevl)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b2d0c99b9c84f62f5f7b259524e0f4b1c9d38318
|
||||
Subproject commit ca166b3446d5bb8b5fb8c6f637ca3f9cb0a8ff3b
|
|
@ -1 +1 @@
|
|||
Subproject commit be682b2cc13125147bad9ebe544f0bad25d0bd22
|
||||
Subproject commit 6328d026f6fa870c87b18ff93ab6f4b65a869686
|
|
@ -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)
|
||||
|
|
|
@ -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<PFAEDLE_PRECISION>
|
||||
#define LINE util::geo::Line<PFAEDLE_PRECISION>
|
||||
#define BOX util::geo::Box<PFAEDLE_PRECISION>
|
||||
#define POLYLINE util::geo::PolyLine<PFAEDLE_PRECISION>
|
||||
#define POINT util::geo::Point<PFDL_PREC>
|
||||
#define LINE util::geo::Line<PFDL_PREC>
|
||||
#define BOX util::geo::Box<PFDL_PREC>
|
||||
#define POLYLINE util::geo::PolyLine<PFDL_PREC>
|
||||
|
||||
#define BOX_PADDING 2500
|
||||
|
||||
|
|
|
@ -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<pfaedle::gtfs::Feed> gtfs(cfg.feedPaths.size());
|
||||
// feed containing the shapes in memory for evaluation
|
||||
ad::cppgtfs::gtfs::Feed evalFeed;
|
||||
|
||||
std::vector<std::string> 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<int>(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<int>(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<int>(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<int>(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<pfaedle::osm::OsmReadOpts> 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<pfaedle::osm::OsmReadOpts> opts;
|
||||
|
@ -205,11 +223,6 @@ int main(int argc, char** argv) {
|
|||
exit(static_cast<int>(RetCode::NO_INPUT_FEED));
|
||||
}
|
||||
|
||||
std::vector<double> 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<ExpoTransWeightNoHeur>();
|
||||
else
|
||||
router = new RouterImpl<ExpoTransWeight>();
|
||||
} else if (motCfg.routingOpts.transPenMethod == "distdiff") {
|
||||
if (cfg.noAStar)
|
||||
router = new RouterImpl<DistDiffTransWeightNoHeur>();
|
||||
else
|
||||
router = new RouterImpl<DistDiffTransWeight>();
|
||||
} else if (motCfg.routingOpts.transPenMethod == "timenorm") {
|
||||
if (cfg.noAStar)
|
||||
router = new RouterImpl<NormDistrTransWeightNoHeur>();
|
||||
else
|
||||
router = new RouterImpl<NormDistrTransWeight>();
|
||||
} 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<int>(RetCode::SUCCESS));
|
||||
}
|
||||
|
||||
pfaedle::netgraph::Graph ng;
|
||||
shapeBuilder.shape(&ng);
|
||||
if (singleTrip) exit(static_cast<int>(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<int>(RetCode::GTFS_WRITE_ERR));
|
||||
}
|
||||
|
@ -329,12 +395,10 @@ std::vector<std::string> getCfgPaths(const Config& cfg) {
|
|||
if (cfg.configPaths.size()) return cfg.configPaths;
|
||||
std::vector<std::string> 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<std::string> 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;
|
||||
|
|
|
@ -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 <brosi@informatik.uni-freiburg.de>";
|
||||
|
@ -28,7 +29,7 @@ static const char* AUTHORS = "Patrick Brosi <brosi@informatik.uni-freiburg.de>";
|
|||
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) << " "
|
||||
<< " <dbg-path>/graph.json\n"
|
||||
<< std::setw(35) << " --write-cgraph"
|
||||
<< "if -T is set, write combination graph as\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " GeoJSON to "
|
||||
"<dbg-path>/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 <arg>, 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";
|
||||
|
|
|
@ -17,20 +17,11 @@ struct MotConfig {
|
|||
router::MOTs mots;
|
||||
osm::OsmReadOpts osmBuildOpts;
|
||||
router::RoutingOpts routingOpts;
|
||||
std::map<std::string, std::string> 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
|
||||
|
|
|
@ -2,28 +2,33 @@
|
|||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#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<std::string>& paths) {
|
||||
void MotConfigReader::parse(const std::vector<std::string>& paths,
|
||||
const std::string& literal) {
|
||||
ConfigFileParser p;
|
||||
|
||||
// parse explicitely given paths
|
||||
|
@ -32,17 +37,33 @@ void MotConfigReader::parse(const std::vector<std::string>& 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<std::string> 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<std::string>& 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<uint32_t>::max()) {
|
||||
val = std::numeric_limits<uint32_t>::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<uint32_t>::max()) {
|
||||
val = std::numeric_limits<uint32_t>::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<uint32_t>::max()) {
|
||||
val = std::numeric_limits<uint32_t>::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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ using ad::cppgtfs::gtfs::Route;
|
|||
class MotConfigReader {
|
||||
public:
|
||||
MotConfigReader();
|
||||
void parse(const std::vector<std::string>& paths);
|
||||
void parse(const std::vector<std::string>& paths, const std::string& literal);
|
||||
|
||||
const std::vector<MotConfig>& getConfigs() const;
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
#ifndef PFAEDLE_CONFIG_PFAEDLECONFIG_H_
|
||||
#define PFAEDLE_CONFIG_PFAEDLECONFIG_H_
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#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<std::string> feedPaths;
|
||||
std::vector<std::string> configPaths;
|
||||
std::set<Route::TYPE> 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) {
|
||||
|
|
|
@ -1,417 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <csignal>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#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<double>& 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<double> oldDists;
|
||||
LINE oldL = getWebMercLine(
|
||||
oldS, t->getStopTimes().begin()->getShapeDistanceTravelled(),
|
||||
(--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists);
|
||||
|
||||
std::vector<double> 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<double>(unmatchedSegments) /
|
||||
static_cast<double>(oldSegs.size())));
|
||||
_resultsAL.insert(Result(t, unmatchedSegmentsLength / totL));
|
||||
|
||||
LOG(DEBUG) << "This result (" << t->getId()
|
||||
<< "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size()
|
||||
<< " = "
|
||||
<< static_cast<double>(unmatchedSegments) /
|
||||
static_cast<double>(oldSegs.size())
|
||||
<< " A_L/L = " << unmatchedSegmentsLength << "/" << totL << " = "
|
||||
<< unmatchedSegmentsLength / totL << " d_f = " << fd;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::vector<LINE> Collector::segmentize(
|
||||
const Trip* t, const LINE& shape, const std::vector<double>& dists,
|
||||
const std::vector<double>* newTripDists) {
|
||||
std::vector<LINE> ret;
|
||||
|
||||
if (t->getStopTimes().size() < 2) return ret;
|
||||
|
||||
POLYLINE pl(shape);
|
||||
std::vector<std::pair<POINT, double> > cuts;
|
||||
|
||||
size_t i = 0;
|
||||
for (auto st : t->getStopTimes()) {
|
||||
if (newTripDists) {
|
||||
cuts.push_back(std::pair<POINT, double>(
|
||||
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(st.getStop()->getLat(),
|
||||
st.getStop()->getLng()),
|
||||
(*newTripDists)[i]));
|
||||
} else {
|
||||
cuts.push_back(std::pair<POINT, double>(
|
||||
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(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<double>* 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<PFAEDLE_PRECISION>(p.lat, p.lng);
|
||||
|
||||
ret.push_back(mercP);
|
||||
if (dists) dists->push_back(p.travelDist);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<Result>& Collector::getResults() const { return _results; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double Collector::getAvgDist() const { return _fdSum / _results.size(); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Collector::printHisto(std::ostream* os, const std::set<Result>& result,
|
||||
const std::vector<double>& bins) const {
|
||||
size_t W = 60;
|
||||
|
||||
auto it = result.begin();
|
||||
std::vector<std::pair<double, size_t> > res;
|
||||
std::vector<const Trip*> 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<double, size_t>(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<double>(r.second) / static_cast<double>(maxC));
|
||||
i++) {
|
||||
(*os) << "|";
|
||||
}
|
||||
|
||||
if (r.second)
|
||||
(*os) << " (" << r.second << ", e.g. #" << examples[j]->getId() << ")";
|
||||
(*os) << std::endl;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::vector<double> Collector::getBins(double mind, double maxd, size_t steps) {
|
||||
double bin = (maxd - mind) / steps;
|
||||
double curE = mind + bin;
|
||||
|
||||
std::vector<double> ret;
|
||||
while (curE <= maxd) {
|
||||
ret.push_back(curE);
|
||||
curE += bin;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Collector::printCsv(std::ostream* os, const std::set<Result>& result,
|
||||
const std::vector<double>& bins) const {
|
||||
auto it = result.begin();
|
||||
std::vector<std::pair<double, size_t> > 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<double, size_t>(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<double> 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<size_t, double> Collector::getDa(const std::vector<LINE>& a,
|
||||
const std::vector<LINE>& b) {
|
||||
assert(a.size() == b.size());
|
||||
std::pair<size_t, double> 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;
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
#define PFAEDLE_GTFS_FEED_H_
|
||||
|
||||
#include <string>
|
||||
#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<StopTime<ad::cppgtfs::gtfs::Stop>, Service,
|
||||
Route, Shape>
|
||||
ad::cppgtfs::gtfs::Route, Shape>
|
||||
Trip;
|
||||
|
||||
} // namespace gtfs
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_GTFS_ROUTE_H_
|
||||
#define PFAEDLE_GTFS_ROUTE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#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_
|
|
@ -45,15 +45,15 @@ bool ShapeContainer<T>::remove(const std::string& id) {
|
|||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
T* ShapeContainer<T>::get(const std::string& id) {
|
||||
if (!has(id)) return 0;
|
||||
return reinterpret_cast<T*>(1);
|
||||
UNUSED(id);
|
||||
return reinterpret_cast<T*>(0);
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
const T* ShapeContainer<T>::get(const std::string& id) const {
|
||||
if (!has(id)) return 0;
|
||||
return reinterpret_cast<T*>(1);
|
||||
UNUSED(id);
|
||||
return reinterpret_cast<T*>(0);
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
|
|
|
@ -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 <typename StopTimeT>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace netgraph {
|
|||
class EdgePL {
|
||||
public:
|
||||
EdgePL() {}
|
||||
EdgePL(const LINE& l, const std::set<const Trip*>& trips)
|
||||
EdgePL(const LINE& l, const std::vector<const Trip*>& 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<const Trip*> _trips;
|
||||
std::vector<const Trip*> _trips;
|
||||
std::set<std::string> _routeShortNames;
|
||||
std::set<std::string> _tripShortNames;
|
||||
};
|
||||
|
|
|
@ -31,10 +31,10 @@ bool BBoxIdx::contains(const Point<double>& p) const {
|
|||
// _____________________________________________________________________________
|
||||
BOX BBoxIdx::getFullWebMercBox() const {
|
||||
return BOX(
|
||||
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(
|
||||
_root.box.getLowerLeft().getY(), _root.box.getLowerLeft().getX()),
|
||||
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(
|
||||
_root.box.getUpperRight().getY(), _root.box.getUpperRight().getX()));
|
||||
util::geo::latLngToWebMerc<PFDL_PREC>(_root.box.getLowerLeft().getY(),
|
||||
_root.box.getLowerLeft().getX()),
|
||||
util::geo::latLngToWebMerc<PFDL_PREC>(_root.box.getUpperRight().getY(),
|
||||
_root.box.getUpperRight().getX()));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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<StatInfo> getStatInfo(Node* node, osmid nid, const POINT& pos,
|
||||
const AttrMap& m, StAttrGroups* groups,
|
||||
Nullable<StatInfo> 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<Node*> 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<TransitEdgeLine*> getLines(const std::vector<size_t>& 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<TransitEdgeLine, TransitEdgeLine*> _lines;
|
||||
std::map<size_t, TransitEdgeLine*> _relLines;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<std::string> 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
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <string>
|
||||
#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<BLOOMF_BITS>();
|
||||
_bitsetNotIn = new std::bitset<BLOOMF_BITS>();
|
||||
_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("", "");
|
||||
|
|
|
@ -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<BLOOMF_BITS>* _bitset;
|
||||
|
||||
std::bitset<BLOOMF_BITS>* _bitsetNotIn;
|
||||
|
||||
mutable std::vector<osmid> _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;
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
#ifndef PFAEDLE_OSM_OSMREADOPTS_H_
|
||||
#define PFAEDLE_OSM_OSMREADOPTS_H_
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#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<std::string, std::vector<trgraph::StatGroup*>>>
|
||||
StAttrGroups;
|
||||
|
||||
struct OsmReadOpts {
|
||||
OsmReadOpts() {}
|
||||
|
||||
|
@ -121,7 +107,7 @@ struct OsmReadOpts {
|
|||
MultAttrMap twoWayFilter;
|
||||
MultAttrMap stationFilter;
|
||||
MultAttrMap stationBlockerFilter;
|
||||
std::vector<StatGroupNAttrRule> statGroupNAttrRules;
|
||||
MultAttrMap turnCycleFilter;
|
||||
|
||||
trgraph::Normalizer statNormzer;
|
||||
trgraph::Normalizer lineNormzer;
|
||||
|
@ -136,14 +122,23 @@ struct OsmReadOpts {
|
|||
uint8_t maxSnapLevel;
|
||||
|
||||
double maxAngleSnapReach;
|
||||
std::vector<double> 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<double> 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_EDGEPL_H_
|
||||
#define PFAEDLE_ROUTER_EDGEPL_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#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_
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#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<router::NodePL, router::EdgePL> Edge;
|
||||
typedef util::graph::Node<router::NodePL, router::EdgePL> Node;
|
||||
typedef util::graph::DirGraph<router::NodePL, router::EdgePL> Graph;
|
||||
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_GRAPH_H_
|
40
src/pfaedle/router/HopCache.cpp
Normal file
40
src/pfaedle/router/HopCache.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2020, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <utility>
|
||||
#include <set>
|
||||
#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<Edge*>& b, uint32_t val) {
|
||||
for (auto eb : b) _cache.set(a, eb, val);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HopCache::setMin(const std::set<Edge*>& a, const Edge* b, uint32_t val) {
|
||||
for (auto ea : a) _cache.set(ea, b, val);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::pair<uint32_t, bool> 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};
|
||||
}
|
39
src/pfaedle/router/HopCache.h
Normal file
39
src/pfaedle/router/HopCache.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2020, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_HOPCACHE_H_
|
||||
#define PFAEDLE_ROUTER_HOPCACHE_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#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<trgraph::Edge*>& b,
|
||||
uint32_t val);
|
||||
|
||||
void setMin(const std::set<trgraph::Edge*>& a, const trgraph::Edge* b,
|
||||
uint32_t val);
|
||||
|
||||
void setEx(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val);
|
||||
|
||||
std::pair<uint32_t, bool> get(const trgraph::Edge* a,
|
||||
const trgraph::Edge* b) const;
|
||||
|
||||
private:
|
||||
util::SparseMatrix<const trgraph::Edge*, int64_t, 0> _cache;
|
||||
};
|
||||
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_HOPCACHE_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<size_t> 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 <typename F>
|
||||
inline bool angSmaller(const Point<F>& f, const Point<F>& m, const Point<F>& 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<trgraph::Node*> NodeSet;
|
||||
typedef std::set<trgraph::Edge*> EdgeSet;
|
||||
typedef std::unordered_map<const Stop*, trgraph::Node*> FeedStops;
|
||||
|
||||
typedef std::vector<NodeCand> NodeCandGroup;
|
||||
typedef std::vector<NodeCandGroup> NodeCandRoute;
|
||||
|
||||
typedef std::vector<EdgeCand> EdgeCandGroup;
|
||||
typedef std::vector<EdgeCandGroup> EdgeCandMap;
|
||||
typedef std::vector<EdgeCandGroup> EdgeCandRoute;
|
||||
|
||||
typedef std::vector<trgraph::Edge*> EdgeList;
|
||||
|
@ -154,8 +97,12 @@ typedef std::vector<trgraph::Node*> 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<EdgeListHop> EdgeListHops;
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_NODEPL_H_
|
||||
#define PFAEDLE_ROUTER_NODEPL_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#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_
|
|
@ -1,646 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#else
|
||||
#define omp_get_thread_num() 0
|
||||
#define omp_get_num_procs() 1
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#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<trgraph::Node*>& 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<trgraph::Edge*>& 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<trgraph::Edge*>& 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<trgraph::Node*>& 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<trgraph::Edge *> 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<trgraph::Node *> 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<trgraph::Node *> from, to;
|
||||
|
||||
if (i == 0)
|
||||
for (auto c : route[i]) from.insert(c.nd);
|
||||
else
|
||||
from.insert(const_cast<trgraph::Node*>(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<trgraph::Edge*> 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<trgraph::Edge*, router::Edge*> edges;
|
||||
std::map<trgraph::Edge*, double> pens;
|
||||
std::unordered_map<trgraph::Edge*, EdgeList*> edgeLists;
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost> 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<double>(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<router::Edge*> 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<trgraph::Edge*>& froms,
|
||||
const std::set<trgraph::Edge*> tos,
|
||||
const trgraph::StatGroup* tgGrp,
|
||||
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest, HopBand hopB) const {
|
||||
std::set<trgraph::Edge*> 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<trgraph::Edge*>& 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<pfaedle::trgraph::Edge*> Router::getCachedHops(
|
||||
trgraph::Edge* from, const std::set<trgraph::Edge*>& tos,
|
||||
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
|
||||
const RoutingAttrs& rAttrs) const {
|
||||
std::set<trgraph::Edge*> 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<EdgeCost, EdgeList>(c, *edges);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
size_t Router::getCacheNumber() const { return _cache.size(); }
|
|
@ -6,198 +6,97 @@
|
|||
#define PFAEDLE_ROUTER_ROUTER_H_
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#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<const trgraph::Edge*, router::Node*> CombNodeMap;
|
||||
constexpr static uint32_t ROUTE_INF = std::numeric_limits<uint32_t>::max();
|
||||
constexpr static double DBL_INF = std::numeric_limits<double>::infinity();
|
||||
constexpr static size_t NO_PREDE = std::numeric_limits<size_t>::max();
|
||||
|
||||
constexpr static int MAX_ROUTE_COST_DOUBLING_STEPS = 3;
|
||||
|
||||
typedef std::pair<size_t, size_t> HId;
|
||||
typedef std::map<
|
||||
RoutingAttrs,
|
||||
std::unordered_map<const trgraph::Edge*,
|
||||
std::unordered_map<const trgraph::Edge*,
|
||||
std::pair<EdgeCost, EdgeList> > > >
|
||||
Cache;
|
||||
typedef std::vector<double> LayerCostsDAG;
|
||||
typedef std::vector<LayerCostsDAG> CostsDAG;
|
||||
typedef std::vector<std::vector<size_t>> PredeDAG;
|
||||
|
||||
struct HopBand {
|
||||
double minD;
|
||||
double maxD;
|
||||
const trgraph::Edge* nearest;
|
||||
double maxInGrpDist;
|
||||
};
|
||||
typedef std::unordered_map<const trgraph::Edge*,
|
||||
std::unordered_map<const trgraph::Edge*, uint32_t>>
|
||||
EdgeCostMatrix;
|
||||
typedef std::unordered_map<const trgraph::Edge*,
|
||||
std::unordered_map<const trgraph::Edge*, double>>
|
||||
EdgeDistMatrix;
|
||||
typedef util::graph::EDijkstra::EList<trgraph::NodePL, trgraph::EdgePL> TrEList;
|
||||
|
||||
struct CostFunc
|
||||
: public EDijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
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<std::pair<std::pair<size_t, size_t>, 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<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
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<double>::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<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
|
||||
const std::set<trgraph::Edge*>& tos);
|
||||
|
||||
const RoutingOpts& _rOpts;
|
||||
uint8_t _lvl;
|
||||
POINT _center;
|
||||
double _maxCentD;
|
||||
EdgeCost operator()(const trgraph::Edge* a,
|
||||
const std::set<trgraph::Edge*>& b) const;
|
||||
};
|
||||
|
||||
struct NDistHeur
|
||||
: public Dijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
NDistHeur(const RoutingOpts& rOpts, const std::set<trgraph::Node*>& tos);
|
||||
|
||||
const RoutingOpts& _rOpts;
|
||||
POINT _center;
|
||||
double _maxCentD;
|
||||
EdgeCost operator()(const trgraph::Node* a,
|
||||
const std::set<trgraph::Node*>& b) const;
|
||||
};
|
||||
|
||||
struct CombCostFunc
|
||||
: public EDijkstra::CostFunc<router::NodePL, router::EdgePL, double> {
|
||||
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<double>::infinity(); }
|
||||
class Router {
|
||||
public:
|
||||
virtual ~Router() = default;
|
||||
virtual std::map<size_t, EdgeListHops> 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 <typename TW>
|
||||
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<size_t, EdgeListHops> route(
|
||||
const TripTrie* trie, const EdgeCandMap& ecm, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest, HopCache* hopCache, bool noFastHops) const;
|
||||
|
||||
private:
|
||||
mutable std::vector<Cache*> _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<trgraph::Edge*>& froms,
|
||||
const std::set<trgraph::Edge*> to, const trgraph::StatGroup* tgGrp,
|
||||
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost>* 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<trgraph::Edge*> getCachedHops(
|
||||
trgraph::Edge* from, const std::set<trgraph::Edge*>& to,
|
||||
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost>* 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<trgraph::Edge*>& froms,
|
||||
const CostFunc& cost, const RoutingAttrs& rAttrs) const;
|
||||
bool cacheDrop(
|
||||
|
||||
bool compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const;
|
||||
HopCache* hopCache, const std::set<trgraph::Edge*>& 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
|
||||
|
||||
|
|
614
src/pfaedle/router/Router.tpp
Normal file
614
src/pfaedle/router/Router.tpp
Normal file
|
@ -0,0 +1,614 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#else
|
||||
#define omp_get_thread_num() 0
|
||||
#define omp_get_num_procs() 1
|
||||
#endif
|
||||
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
#include <stack>
|
||||
|
||||
using util::graph::EDijkstra;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename TW>
|
||||
std::map<size_t, EdgeListHops> RouterImpl<TW>::route(
|
||||
const TripTrie* trie, const EdgeCandMap& ecm, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest, HopCache* hopCache, bool noFastHops) const {
|
||||
std::map<size_t, EdgeListHops> ret;
|
||||
|
||||
// the current node costs in our DAG
|
||||
CostsDAG costsDAG(trie->getNds().size());
|
||||
PredeDAG predeDAG(trie->getNds().size());
|
||||
std::vector<double> 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<size_t> 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<uint32_t>::max() / 2)
|
||||
newMaxCost *= 2;
|
||||
else
|
||||
newMaxCost = std::numeric_limits<uint32_t>::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<size_t, double> sinkCosts;
|
||||
std::unordered_map<size_t, size_t> 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<trgraph::Edge*> 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<uint32_t>::max()) {
|
||||
maxCostRtInt = std::numeric_limits<uint32_t>::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 <typename TW>
|
||||
void RouterImpl<TW>::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<trgraph::Edge*> eFrs;
|
||||
for (const auto& from : froms) {
|
||||
if (!from.e) continue;
|
||||
eFrs.insert(from.e);
|
||||
}
|
||||
|
||||
std::set<trgraph::Edge*> 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<trgraph::Edge*> remTos;
|
||||
for (trgraph::Edge* eTo : eTos) {
|
||||
// init ecmDist
|
||||
ecmDist[eFrom][eTo] = ROUTE_INF;
|
||||
|
||||
std::pair<uint32_t, bool> 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<trgraph::Edge*, TrEList> paths;
|
||||
std::unordered_map<trgraph::Edge*, TrEList*> 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 <typename TW>
|
||||
void RouterImpl<TW>::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<trgraph::Edge*, uint32_t> initCosts;
|
||||
|
||||
std::set<trgraph::Edge*> eFrs, eTos;
|
||||
std::map<trgraph::Edge*, std::vector<size_t>> 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 <fr>
|
||||
|
||||
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<trgraph::Edge*, TrEList> paths;
|
||||
std::unordered_map<trgraph::Edge*, TrEList*> 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 <typename TW>
|
||||
bool RouterImpl<TW>::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 <typename TW>
|
||||
bool RouterImpl<TW>::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 <typename TW>
|
||||
bool RouterImpl<TW>::cacheDrop(HopCache* hopCache,
|
||||
const std::set<trgraph::Edge*>& 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 <typename TW>
|
||||
uint32_t RouterImpl<TW>::addNonOverflow(uint32_t a, uint32_t b) const {
|
||||
if (a == std::numeric_limits<uint32_t>::max() ||
|
||||
b == std::numeric_limits<uint32_t>::max())
|
||||
return std::numeric_limits<uint32_t>::max();
|
||||
uint32_t res = a + b;
|
||||
if (res >= a && res >= b) return res;
|
||||
return std::numeric_limits<uint32_t>::max();
|
||||
}
|
|
@ -5,8 +5,10 @@
|
|||
#ifndef PFAEDLE_ROUTER_ROUTINGATTRS_H_
|
||||
#define PFAEDLE_ROUTER_ROUTINGATTRS_H_
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#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<std::string> lineTo;
|
||||
std::string shortName;
|
||||
|
||||
mutable std::map<const TransitEdgeLine*, double> _simiCache;
|
||||
const pfaedle::statsimiclassifier::StatsimiClassifier* classifier;
|
||||
|
||||
mutable std::unordered_map<const TransitEdgeLine*, LineSimilarity> _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
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,39 +9,41 @@
|
|||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#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<Trip*> Cluster;
|
||||
typedef std::vector<Cluster> Clusters;
|
||||
typedef std::pair<const Stop*, const Stop*> StopPair;
|
||||
typedef std::unordered_map<const Trip*, router::RoutingAttrs> TripRAttrs;
|
||||
typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
|
||||
typedef std::vector<TripTrie> TripForest;
|
||||
typedef std::map<router::RoutingAttrs, TripForest> TripForests;
|
||||
typedef std::pair<const ad::cppgtfs::gtfs::Stop*,
|
||||
const ad::cppgtfs::gtfs::Stop*>
|
||||
StopPair;
|
||||
typedef std::unordered_map<const pfaedle::gtfs::Trip*, router::RoutingAttrs>
|
||||
TripRAttrs;
|
||||
typedef std::unordered_map<const trgraph::Edge*,
|
||||
std::vector<const pfaedle::gtfs::Trip*>>
|
||||
TrGraphEdgs;
|
||||
typedef std::map<Route*, std::map<uint32_t, std::vector<gtfs::Trip*>>>
|
||||
RouteRefColors;
|
||||
typedef std::unordered_map<const ad::cppgtfs::gtfs::Stop*, EdgeCandGroup>
|
||||
GrpCache;
|
||||
|
||||
/*
|
||||
* Layer class for the router. Provides an interface for direct usage with
|
||||
|
@ -49,76 +51,116 @@ typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
|
|||
*/
|
||||
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<std::vector<LINE>, 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<size_t, EdgeListHops> 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<float>* hopDists,
|
||||
uint32_t* bestColor);
|
||||
|
||||
ad::cppgtfs::gtfs::Shape getGtfsShape(const Shape& shp, Trip* t,
|
||||
std::vector<double>* hopDists);
|
||||
void setShape(pfaedle::gtfs::Trip* t, const ad::cppgtfs::gtfs::Shape& s,
|
||||
const std::vector<float>& dists);
|
||||
|
||||
void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s,
|
||||
const std::vector<double>& 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<double> getTransTimes(pfaedle::gtfs::Trip* trip) const;
|
||||
std::vector<double> 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<size_t, router::EdgeListHops> route(const TripTrie* trie,
|
||||
const EdgeCandMap& ecm,
|
||||
HopCache* hopCache) const;
|
||||
void buildCandCache(const TripForests& clusters);
|
||||
void buildIndex();
|
||||
|
||||
std::vector<LINE> getGeom(const EdgeListHops& shp, const RoutingAttrs& rAttrs,
|
||||
std::map<uint32_t, double>* colors) const;
|
||||
double timePen(int candTime, int schedTime) const;
|
||||
|
||||
LINE getLine(const EdgeListHop& hop, const RoutingAttrs&,
|
||||
std::map<uint32_t, double>* colMap) const;
|
||||
LINE getLine(const trgraph::Edge* edg) const;
|
||||
std::vector<float> getMeasure(const std::vector<LINE>& lines) const;
|
||||
|
||||
trgraph::Edge* deg2reachable(trgraph::Edge* e,
|
||||
std::set<trgraph::Edge*> edgs) const;
|
||||
|
||||
EdgeCandGroup timeExpand(const EdgeCand& ec, int time) const;
|
||||
|
||||
std::set<uint32_t> 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<pfaedle::gtfs::Trip*>& trips) const;
|
||||
|
||||
void shapeWorker(
|
||||
const std::vector<const TripForest*>* tries, std::atomic<size_t>* at,
|
||||
std::map<std::string, size_t>* shpUsage,
|
||||
std::map<Route*, std::map<uint32_t, std::vector<gtfs::Trip*>>>*,
|
||||
TrGraphEdgs* gtfsGraph);
|
||||
|
||||
void edgCandWorker(std::vector<const Stop*>* stops, GrpCache* cache);
|
||||
void clusterWorker(const std::vector<RoutingAttrs>* rAttrs,
|
||||
const std::map<RoutingAttrs, std::vector<Trip*>>* trips,
|
||||
TripForests* forest);
|
||||
|
||||
pfaedle::trgraph::EdgeGrid _eGrid;
|
||||
pfaedle::trgraph::NodeGrid _nGrid;
|
||||
};
|
||||
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
|
|
33
src/pfaedle/router/Stats.h
Normal file
33
src/pfaedle/router/Stats.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_STATS_H_
|
||||
#define PFAEDLE_ROUTER_STATS_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#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_
|
219
src/pfaedle/router/TripTrie.cpp
Normal file
219
src/pfaedle/router/TripTrie.cpp
Normal file
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<PFDL_PREC>(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<PFDL_PREC>(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<pfaedle::router::TripTrieNd>& 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<size_t, std::vector<pfaedle::gtfs::Trip*>>&
|
||||
TripTrie::getNdTrips() const {
|
||||
return _ndTrips;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const pfaedle::router::TripTrieNd& TripTrie::getNd(size_t nid) const {
|
||||
return _nds[nid];
|
||||
}
|
65
src/pfaedle/router/TripTrie.h
Normal file
65
src/pfaedle/router/TripTrie.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_TRIPTRIE_H_
|
||||
#define PFAEDLE_ROUTER_TRIPTRIE_H_
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#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<size_t> 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<TripTrieNd>& 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<size_t, std::vector<pfaedle::gtfs::Trip*>>& getNdTrips() const;
|
||||
|
||||
private:
|
||||
std::vector<TripTrieNd> _nds;
|
||||
std::map<pfaedle::gtfs::Trip*, size_t> _tripNds;
|
||||
std::map<size_t, std::vector<pfaedle::gtfs::Trip*>> _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_
|
261
src/pfaedle/router/Weights.cpp
Normal file
261
src/pfaedle/router/Weights.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <limits>
|
||||
#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<trgraph::Edge*>& 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<trgraph::Edge*>& 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<uint32_t>::max()) {
|
||||
_lastC = std::numeric_limits<uint32_t>::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<uint32_t>::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<double>(c) * _rOpts.lineUnmatchedPunishFact);
|
||||
} else if (_rOpts.lineUnmatchedPunishFact > 1) {
|
||||
double a =
|
||||
std::round(static_cast<double>(c) * _rOpts.lineUnmatchedPunishFact);
|
||||
if (a > std::numeric_limits<uint32_t>::max())
|
||||
return std::numeric_limits<uint32_t>::max();
|
||||
c = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (!simi.fromSimilar) {
|
||||
if (_rOpts.lineNameFromUnmatchedPunishFact < 1) {
|
||||
c = std::ceil(static_cast<double>(c) *
|
||||
_rOpts.lineNameFromUnmatchedPunishFact);
|
||||
} else if (_rOpts.lineNameFromUnmatchedPunishFact > 1) {
|
||||
double a = std::round(static_cast<double>(c) *
|
||||
_rOpts.lineNameFromUnmatchedPunishFact);
|
||||
if (a > std::numeric_limits<uint32_t>::max())
|
||||
return std::numeric_limits<uint32_t>::max();
|
||||
c = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (!simi.toSimilar) {
|
||||
if (_rOpts.lineNameToUnmatchedPunishFact < 1) {
|
||||
c = std::ceil(static_cast<double>(c) *
|
||||
_rOpts.lineNameToUnmatchedPunishFact);
|
||||
} else if (_rOpts.lineNameToUnmatchedPunishFact > 1) {
|
||||
double a = std::round(static_cast<double>(c) *
|
||||
_rOpts.lineNameToUnmatchedPunishFact);
|
||||
if (a > std::numeric_limits<uint32_t>::max())
|
||||
return std::numeric_limits<uint32_t>::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<uint32_t>::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<uint32_t>::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<uint32_t>::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<double>(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<double>(cs) / 10.0;
|
||||
|
||||
// standard deviation of normal distribution
|
||||
double standarddev = 1;
|
||||
|
||||
// no backwards time travel!
|
||||
if (t0 < 0) return std::numeric_limits<double>::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<uint32_t>::max();
|
||||
}
|
161
src/pfaedle/router/Weights.h
Normal file
161
src/pfaedle/router/Weights.h
Normal file
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_WEIGHTS_H_
|
||||
#define PFAEDLE_ROUTER_WEIGHTS_H_
|
||||
|
||||
#include <set>
|
||||
#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<trgraph::NodePL, trgraph::EdgePL,
|
||||
uint32_t>
|
||||
RCostFunc;
|
||||
typedef util::graph::EDijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL,
|
||||
uint32_t>
|
||||
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<trgraph::Edge*>& tos);
|
||||
|
||||
const RoutingOpts& _rOpts;
|
||||
double _maxV;
|
||||
POINT _center;
|
||||
double _maxCentD;
|
||||
uint32_t operator()(const trgraph::Edge* a,
|
||||
const std::set<trgraph::Edge*>& 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<trgraph::Edge*>& tos) {
|
||||
UNUSED(maxV);
|
||||
UNUSED(rOpts);
|
||||
UNUSED(tos);
|
||||
}
|
||||
|
||||
uint32_t operator()(const trgraph::Edge* a,
|
||||
const std::set<trgraph::Edge*>& 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<trgraph::Edge*>& tos) {
|
||||
UNUSED(maxV);
|
||||
UNUSED(rOpts);
|
||||
UNUSED(tos);
|
||||
}
|
||||
|
||||
uint32_t operator()(const trgraph::Edge* a,
|
||||
const std::set<trgraph::Edge*>& 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<trgraph::Edge*>& tos) {
|
||||
UNUSED(maxV);
|
||||
UNUSED(rOpts);
|
||||
UNUSED(tos);
|
||||
}
|
||||
|
||||
uint32_t operator()(const trgraph::Edge* a,
|
||||
const std::set<trgraph::Edge*>& b) const {
|
||||
UNUSED(a);
|
||||
UNUSED(b);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_WEIGHTS_H_
|
30
src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp
Normal file
30
src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <codecvt>
|
||||
#include <exception>
|
||||
#include <locale>
|
||||
#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
|
||||
}
|
35
src/pfaedle/statsimi-classifier/StatsimiClassifier.h
Normal file
35
src/pfaedle/statsimi-classifier/StatsimiClassifier.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2020, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_
|
||||
#define PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_
|
||||
|
||||
#include <string>
|
||||
#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_
|
2
src/pfaedle/tests/CMakeLists.txt
Normal file
2
src/pfaedle/tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
add_executable(pfaedleTest TestMain.cpp)
|
||||
target_link_libraries(pfaedleTest pfaedle_dep util)
|
329
src/pfaedle/tests/TestMain.cpp
Normal file
329
src/pfaedle/tests/TestMain.cpp
Normal file
|
@ -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<ExpoTransWeight> 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);
|
||||
}
|
|
@ -11,15 +11,12 @@
|
|||
using pfaedle::trgraph::EdgePL;
|
||||
using pfaedle::trgraph::TransitEdgeLine;
|
||||
|
||||
|
||||
std::map<LINE*, size_t> EdgePL::_flines;
|
||||
std::map<const TransitEdgeLine*, size_t> 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<const TransitEdgeLine*>& 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<int>(_oneWay));
|
||||
obj["cost"] = std::to_string(static_cast<double>(_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; }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ namespace trgraph {
|
|||
typedef util::graph::Edge<NodePL, EdgePL> Edge;
|
||||
typedef util::graph::Node<NodePL, EdgePL> Node;
|
||||
typedef util::graph::DirGraph<NodePL, EdgePL> Graph;
|
||||
typedef Grid<Node*, Point, PFAEDLE_PRECISION> NodeGrid;
|
||||
typedef Grid<Edge*, Line, PFAEDLE_PRECISION> EdgeGrid;
|
||||
typedef Grid<Node*, Point, PFDL_PREC> NodeGrid;
|
||||
typedef Grid<Edge*, Line, PFDL_PREC> EdgeGrid;
|
||||
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
|
|
@ -3,22 +3,19 @@
|
|||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#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<const Component*, size_t> NodePL::_comps;
|
||||
std::vector<Component> NodePL::comps;
|
||||
std::vector<StatInfo> 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<size_t>(_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<size_t>(_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<uint32_t>::max() - 1; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool NodePL::isBlocker() const { return _si == &_blockerSI; }
|
||||
bool NodePL::isTurnCycle() const {
|
||||
return _si == (std::numeric_limits<uint32_t>::max() - 1);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setBlocker() { _si = std::numeric_limits<uint32_t>::max(); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool NodePL::isBlocker() const {
|
||||
return _si == std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#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<Component> 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<const Component*, size_t> _comps;
|
||||
static std::vector<StatInfo> _statInfos;
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
@ -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<std::mutex> lock(_mutex);
|
||||
return norm(sn);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string Normalizer::norm(const std::string& sn) const {
|
||||
auto i = _cache.find(sn);
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
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<std::string, std::string> _cache;
|
||||
mutable std::mutex _mutex;
|
||||
|
||||
void buildRules(const ReplRules& rules);
|
||||
};
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <set>
|
||||
#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<Node*> nds = other->getNodes();
|
||||
std::set<const Stop*> 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<Node*>& 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<Node*>& StatGroup::getNodes() { return _nodes; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<const Stop*>& 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<PFAEDLE_PRECISION>(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)});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_TRGRAPH_STATGROUP_H_
|
||||
#define PFAEDLE_TRGRAPH_STATGROUP_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#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<trgraph::Node*>& getNodes() const;
|
||||
std::set<trgraph::Node*>& getNodes();
|
||||
|
||||
// Return all stops contained in this group
|
||||
const std::set<const Stop*>& 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<trgraph::Node*> _nodes;
|
||||
std::set<const Stop*> _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<const Stop*, router::NodeCandGroup> _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_
|
|
@ -3,66 +3,24 @@
|
|||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#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<const StatGroup*, size_t> 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;
|
||||
|
|
|
@ -6,24 +6,20 @@
|
|||
#define PFAEDLE_TRGRAPH_STATINFO_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
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<std::string> _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<const StatGroup*, size_t> _groups;
|
||||
static void unRefGroup(StatGroup* g);
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
|
15
src/shapevl/CMakeLists.txt
Normal file
15
src/shapevl/CMakeLists.txt
Normal file
|
@ -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)
|
373
src/shapevl/Collector.cpp
Normal file
373
src/shapevl/Collector.cpp
Normal file
|
@ -0,0 +1,373 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#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<double> oldDists;
|
||||
LINE oldL = getWebMercLine(oldS, &oldDists);
|
||||
|
||||
std::vector<double> 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<double>(unmatchedSegments) /
|
||||
static_cast<double>(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<LINE> Collector::segmentize(const Trip* t, const LINE& shape,
|
||||
const std::vector<double>& 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<LINE> ret;
|
||||
|
||||
if (t->getStopTimes().size() < 2) return ret;
|
||||
|
||||
POLYLINE pl(shape);
|
||||
std::vector<std::pair<POINT, double> > cuts;
|
||||
|
||||
size_t i = 0;
|
||||
for (auto st : t->getStopTimes()) {
|
||||
cuts.push_back(std::pair<POINT, double>(
|
||||
util::geo::latLngToWebMerc<PFDL_PREC>(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<double>* dists) {
|
||||
LINE ret;
|
||||
|
||||
for (size_t i = 0; i < s->getPoints().size(); i++) {
|
||||
ret.push_back(util::geo::latLngToWebMerc<PFDL_PREC>(s->getPoints()[i].lat,
|
||||
s->getPoints()[i].lng));
|
||||
(*dists).push_back(s->getPoints()[i].travelDist);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<Result>& Collector::getResults() const { return _results; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double Collector::getAvgDist() const { return _fdSum / _results.size(); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::vector<double> Collector::getBins(double mind, double maxd, size_t steps) {
|
||||
double bin = (maxd - mind) / steps;
|
||||
double curE = mind + bin;
|
||||
|
||||
std::vector<double> ret;
|
||||
while (curE <= maxd) {
|
||||
ret.push_back(curE);
|
||||
curE += bin;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Collector::printCsv(std::ostream* os,
|
||||
const std::set<Result>& result) const {
|
||||
for (auto r : result) (*os) << r.getDist() << "\n";
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double Collector::getAcc() const {
|
||||
return static_cast<double>(_acc0) / static_cast<double>(_results.size());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Collector::printShortStats(std::ostream* os) const {
|
||||
if (_results.size()) {
|
||||
(*os) << "acc-0: "
|
||||
<< (static_cast<double>(_acc0) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %";
|
||||
(*os) << " acc-10: "
|
||||
<< (static_cast<double>(_acc10) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %";
|
||||
(*os) << " acc-20: "
|
||||
<< (static_cast<double>(_acc20) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %";
|
||||
(*os) << " acc-40: "
|
||||
<< (static_cast<double>(_acc40) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %";
|
||||
(*os) << " acc-80: "
|
||||
<< (static_cast<double>(_acc80) /
|
||||
static_cast<double>(_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<double>(_acc0) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %"
|
||||
<< "\n";
|
||||
(*os) << " acc-10: "
|
||||
<< (static_cast<double>(_acc10) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %"
|
||||
<< "\n";
|
||||
(*os) << " acc-20: "
|
||||
<< (static_cast<double>(_acc20) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %"
|
||||
<< "\n";
|
||||
(*os) << " acc-40: "
|
||||
<< (static_cast<double>(_acc40) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %"
|
||||
<< "\n";
|
||||
(*os) << " acc-80: "
|
||||
<< (static_cast<double>(_acc80) /
|
||||
static_cast<double>(_results.size())) *
|
||||
100
|
||||
<< " %"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
(*os) << std::endl;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::pair<size_t, double> Collector::getDa(const std::vector<LINE>& a,
|
||||
const std::vector<LINE>& b) {
|
||||
assert(a.size() == b.size());
|
||||
std::pair<size_t, double> 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;
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef PFAEDLE_EVAL_COLLECTOR_H_
|
||||
#define PFAEDLE_EVAL_COLLECTOR_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
|
@ -12,13 +13,12 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
#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<double>& 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<double>& 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<Result>& 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>& result,
|
||||
const std::vector<double>& 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>& result,
|
||||
const std::vector<double>& bins) const;
|
||||
void printCsv(std::ostream* os, const std::set<Result>& 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<double>* dists);
|
||||
static LINE getWebMercLine(const Shape* s, std::vector<double>* dists);
|
||||
|
||||
double getAcc() const;
|
||||
|
||||
private:
|
||||
std::set<Result> _results;
|
||||
std::set<Result> _resultsAN;
|
||||
std::set<Result> _resultsAL;
|
||||
std::map<const Shape*, std::map<std::string, double> > _dCache;
|
||||
std::map<const Shape*, std::map<std::string, std::pair<size_t, double> > >
|
||||
_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<double> _dfBins;
|
||||
std::ostream* _reportOut;
|
||||
|
||||
static std::pair<size_t, double> getDa(const std::vector<LINE>& a,
|
||||
const std::vector<LINE>& b);
|
||||
|
||||
static std::vector<LINE> segmentize(const Trip* t, const LINE& shape,
|
||||
const std::vector<double>& dists,
|
||||
const std::vector<double>* newTripDists);
|
||||
const std::vector<double>& dists);
|
||||
|
||||
static std::vector<double> getBins(double mind, double maxd, size_t steps);
|
||||
};
|
|
@ -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 {
|
174
src/shapevl/ShapevlMain.cpp
Normal file
174
src/shapevl/ShapevlMain.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
// Copyright 2020, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/Parser.h"
|
||||
#include "shapevl/Collector.h"
|
||||
#include "util/Misc.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
std::atomic<int> count(0);
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void printHelp(int argc, char** argv) {
|
||||
UNUSED(argc);
|
||||
std::cout << "Usage: " << argv[0]
|
||||
<< " [-f <reportpath>] -g <gtfs> [-s] <test feeds>"
|
||||
<< "\n";
|
||||
std::cout
|
||||
<< "\nAllowed arguments:\n -g <gtfs> Ground truth GTFS file\n";
|
||||
std::cout << " -s Only output summary\n";
|
||||
std::cout << " -f <folder> Output full reports (per feed) to <folder>\n";
|
||||
std::cout
|
||||
<< " -m MOTs to match (GTFS MOT or string, default: all)\n";
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void eval(const std::vector<std::string>* paths,
|
||||
std::vector<pfaedle::eval::Collector>* colls,
|
||||
const std::set<Route::TYPE>* 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<std::string> evlFeedPaths;
|
||||
std::set<std::string> evlFeedPathsUniq;
|
||||
std::vector<pfaedle::eval::Collector> evalColls;
|
||||
std::vector<std::ofstream> 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<Route::TYPE> mots =
|
||||
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(util::trim(motStr));
|
||||
|
||||
std::vector<ad::cppgtfs::gtfs::Feed> 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<std::thread> 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);
|
||||
}
|
||||
}
|
||||
}
|
335
src/util/3rdparty/MurmurHash3.cpp
vendored
Normal file
335
src/util/3rdparty/MurmurHash3.cpp
vendored
Normal file
|
@ -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 <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
37
src/util/3rdparty/MurmurHash3.h
vendored
Normal file
37
src/util/3rdparty/MurmurHash3.h
vendored
Normal file
|
@ -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 <stdint.h>
|
||||
|
||||
#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_
|
454
src/util/3rdparty/dtoa_milo.h
vendored
Normal file
454
src/util/3rdparty/dtoa_milo.h
vendored
Normal file
|
@ -0,0 +1,454 @@
|
|||
// based on
|
||||
// https://github.com/miloyip/dtoa-benchmark/blob/master/src/milo/dtoa_milo.h
|
||||
|
||||
#pragma once
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#include "msinttypes/stdint.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
namespace gcc_ints {
|
||||
__extension__ typedef __int128 int128;
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
} // namespace gcc_ints
|
||||
|
||||
#define UINT64_C2(h, l) \
|
||||
((static_cast<uint64_t>(h) << 32) | static_cast<uint64_t>(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<gcc_ints::uint128>(f) *
|
||||
static_cast<gcc_ints::uint128>(rhs.f);
|
||||
uint64_t h = p >> 64;
|
||||
uint64_t l = static_cast<uint64_t>(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<int>(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<int>(dk);
|
||||
if (dk - k > 0.0) k++;
|
||||
|
||||
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||
*K = -(-348 + static_cast<int>(
|
||||
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<uint32_t>(Mp.f >> -one.e);
|
||||
uint64_t p2 = Mp.f & (one.f - 1);
|
||||
int kappa = static_cast<int>(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<char>(d);
|
||||
kappa--;
|
||||
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||
if (tmp <= delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, tmp,
|
||||
static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// kappa = 0
|
||||
for (;;) {
|
||||
p2 *= 10;
|
||||
delta *= 10;
|
||||
char d = static_cast<char>(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<char>(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<char>(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
|
312
src/util/Misc.h
312
src/util/Misc.h
|
@ -9,18 +9,81 @@
|
|||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <immintrin.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#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<microseconds>(t2 - t1).count() / 1000.0)
|
||||
#define TOOK(t1, t2) (std::chrono::duration_cast<std::chrono::microseconds>(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<microseconds>(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0)
|
||||
#define T_STOP(n) (std::chrono::duration_cast<std::chrono::microseconds>(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<bool>(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 <windows.h>
|
||||
#include <psapi.h>
|
||||
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <mach/mach.h>
|
||||
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
|
||||
#include <fcntl.h>
|
||||
#include <procfs.h>
|
||||
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#else
|
||||
#error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS."
|
||||
#endif
|
||||
|
||||
namespace util {
|
||||
|
||||
struct hashPair {
|
||||
template <class T1, class T2>
|
||||
size_t operator()(const std::pair<T1, T2>& p) const {
|
||||
auto h1 = std::hash<T1>{}(p.first);
|
||||
auto h2 = std::hash<T2>{}(p.second);
|
||||
return h1 ^ h2;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Key, typename Val, Val Def>
|
||||
class SparseMatrix {
|
||||
public:
|
||||
Val get(const Key& x, const Key& y) const {
|
||||
auto a = _m.find(std::pair<Key, Key>(x, y));
|
||||
if (a == _m.end()) return Def;
|
||||
return a->second;
|
||||
}
|
||||
|
||||
void set(Key x, Key y, Val v) {
|
||||
_m[std::pair<Key, Key>(x, y)] = v;
|
||||
}
|
||||
|
||||
const std::map<std::pair<Key, Key>, Val>& vals() const {
|
||||
return _m;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::pair<Key, Key>, 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 <typename V>
|
||||
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 <typename V>
|
||||
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 <typename V>
|
||||
size_t inversions(const std::vector<V>& 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<V>(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<float>::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_
|
||||
|
|
39
src/util/PriorityQueue.h
Normal file
39
src/util/PriorityQueue.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2019, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_PRIORITYQUEUE_H_
|
||||
#define UTIL_PRIORITYQUEUE_H_
|
||||
|
||||
#include<iomanip>
|
||||
#include<queue>
|
||||
#include<iostream>
|
||||
|
||||
namespace util {
|
||||
template <typename K, typename V>
|
||||
class PriorityQueue {
|
||||
struct _ByFirst {
|
||||
bool operator()(const std::pair<K, V>& a, const std::pair<K, V>& b) {
|
||||
return a.first > b.first;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
PriorityQueue() : _last(std::numeric_limits<K>::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::pair<K, V>, std::vector<std::pair<K, V>>, _ByFirst>
|
||||
_pq;
|
||||
};
|
||||
#include "util/PriorityQueue.tpp"
|
||||
} // namespace util
|
||||
|
||||
#endif
|
41
src/util/PriorityQueue.tpp
Normal file
41
src/util/PriorityQueue.tpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2019, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename K, typename V>
|
||||
void PriorityQueue<K, V>::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<K, V>(key, val));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename K, typename V>
|
||||
const K PriorityQueue<K, V>::topKey() {
|
||||
_last = _pq.top().first;
|
||||
return _pq.top().first;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename K, typename V>
|
||||
const V& PriorityQueue<K, V>::topVal() {
|
||||
_last = _pq.top().first;
|
||||
return _pq.top().second;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename K, typename V>
|
||||
bool PriorityQueue<K, V>::empty() const {
|
||||
return _pq.empty();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename K, typename V>
|
||||
void PriorityQueue<K, V>::pop() {
|
||||
_pq.pop();
|
||||
}
|
|
@ -6,11 +6,17 @@
|
|||
#define UTIL_STRING_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <codecvt>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
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 <class String>
|
||||
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 <class String>
|
||||
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>(std::string(prefix), std::string(s));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t prefixEditDist(const char* prefix, const char* s,
|
||||
size_t deltaMax) {
|
||||
return prefixEditDist<std::string>(std::string(prefix), std::string(s),
|
||||
deltaMax);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t prefixEditDist(const char* prefix, const std::string& s) {
|
||||
return prefixEditDist<std::string>(std::string(prefix), s);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t prefixEditDist(const char* prefix, const std::string& s,
|
||||
size_t deltaMax) {
|
||||
return prefixEditDist<std::string>(std::string(prefix), s, deltaMax);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t prefixEditDist(const std::string& prefix, const char* s) {
|
||||
return prefixEditDist<std::string>(prefix, std::string(s));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t prefixEditDist(const std::string& prefix, const char* s,
|
||||
size_t deltaMax) {
|
||||
return prefixEditDist<std::string>(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 <typename T>
|
||||
inline std::wstring toWStr(const std::string& str) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.from_bytes(str);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string toNStr(const std::wstring& wstr) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return converter.to_bytes(wstr);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::vector<std::string> tokenize(const std::string& str) {
|
||||
std::vector<std::string> 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<std::string> 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<std::string> 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 <class T>
|
||||
inline std::string implode(const std::vector<T>& vec, const char* del) {
|
||||
return implode(vec.begin(), vec.end(), del);
|
||||
}
|
||||
}
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_STRING_H_
|
||||
|
|
|
@ -64,7 +64,7 @@ const PolyLine<T>& BezierCurve<T>::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;
|
||||
}
|
||||
|
|
41
src/util/geo/CircularSegment.h
Normal file
41
src/util/geo/CircularSegment.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GEO_CIRCULARSEGMENT_H_
|
||||
#define UTIL_GEO_CIRCULARSEGMENT_H_
|
||||
|
||||
#include <vector>
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/geo/PolyLine.h"
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
|
||||
/**
|
||||
* Circular segment
|
||||
*/
|
||||
template <typename T>
|
||||
class CircularSegment {
|
||||
public:
|
||||
CircularSegment(const Point<T>& a, double ang, const Point<T>& c);
|
||||
|
||||
const PolyLine<T>& render(double d);
|
||||
|
||||
private:
|
||||
// store the rendered polyline for quicker access
|
||||
PolyLine<T> _rendered;
|
||||
|
||||
const Point<T>& _a, _c;
|
||||
double _renderD;
|
||||
|
||||
double _ang, _rad, _s, _initAng;
|
||||
|
||||
Point<T> valueAt(double t) const;
|
||||
};
|
||||
|
||||
#include "util/geo/CircularSegment.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GEO_BEZIERCURVE_H_
|
51
src/util/geo/CircularSegment.tpp
Normal file
51
src/util/geo/CircularSegment.tpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
CircularSegment<T>::CircularSegment(const Point<T>& a, double ang,
|
||||
const Point<T>& c) : _a(a), _c(c), _renderD(0), _ang(ang)
|
||||
{
|
||||
|
||||
_rad = dist(a, c);
|
||||
_s = fabs(_ang * _rad);
|
||||
_initAng = angBetween(c, a);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> CircularSegment<T>::valueAt(double ang) const {
|
||||
double xPos = _c.getX() + _rad * cos(ang);
|
||||
double yPos = _c.getY() + _rad * sin(ang);
|
||||
return Point<T>(xPos, yPos);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
const PolyLine<T>& CircularSegment<T>::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;
|
||||
}
|
|
@ -51,6 +51,11 @@ typedef Polygon<int> 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 <typename T>
|
||||
|
@ -203,6 +208,14 @@ inline Polygon<T> move(Polygon<T> geo, double x, double y) {
|
|||
return geo;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> move(Box<T> geo, double x, double y) {
|
||||
geo.setLowerLeft(move(geo.getLowerLeft(), x, y));
|
||||
geo.setUpperRight(move(geo.getUpperRight(), x, y));
|
||||
return geo;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class Geometry, typename T>
|
||||
inline std::vector<Geometry<T>> move(std::vector<Geometry<T>> multigeo,
|
||||
|
@ -356,8 +369,9 @@ inline bool contains(const Polygon<T>& polyC, const Polygon<T>& poly) {
|
|||
}
|
||||
|
||||
// also check the last hop
|
||||
if (!contains(LineSegment<T>(polyC.getOuter().back(), polyC.getOuter().front()),
|
||||
poly))
|
||||
if (!contains(
|
||||
LineSegment<T>(polyC.getOuter().back(), polyC.getOuter().front()),
|
||||
poly))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -627,6 +641,34 @@ inline bool intersects(const Box<T>& b, const Point<T>& p) {
|
|||
return intersects(p, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class GeometryA,
|
||||
template <typename> class GeometryB, typename T>
|
||||
inline bool intersects(const std::vector<GeometryA<T>>& multigeom,
|
||||
const GeometryB<T>& b) {
|
||||
for (const auto& geom : multigeom)
|
||||
if (intersects(geom, b)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class GeometryA,
|
||||
template <typename> class GeometryB, typename T>
|
||||
inline bool intersects(const GeometryB<T>& b,
|
||||
const std::vector<GeometryA<T>>& multigeom) {
|
||||
return intersects(multigeom, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class GeometryA,
|
||||
template <typename> class GeometryB, typename T>
|
||||
inline bool intersects(const std::vector<GeometryA<T>>& multigeomA,
|
||||
const std::vector<GeometryA<T>>& multigeomB) {
|
||||
for (const auto& geom : multigeomA)
|
||||
if (intersects(geom, multigeomB)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> intersection(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x,
|
||||
|
@ -777,6 +819,36 @@ inline double dist(const Line<T>& la, const Line<T>& lb) {
|
|||
return d;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class GeometryA,
|
||||
template <typename> class GeometryB, typename T>
|
||||
inline double dist(const std::vector<GeometryA<T>>& multigeom,
|
||||
const GeometryB<T>& b) {
|
||||
double d = std::numeric_limits<double>::infinity();
|
||||
for (const auto& geom : multigeom)
|
||||
if (dist(geom, b) < d) d = dist(geom, b);
|
||||
return d;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class GeometryA,
|
||||
template <typename> class GeometryB, typename T>
|
||||
inline double dist(const GeometryB<T>& b,
|
||||
const std::vector<GeometryA<T>>& multigeom) {
|
||||
return dist(multigeom, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class GeometryA,
|
||||
template <typename> class GeometryB, typename T>
|
||||
inline double dist(const std::vector<GeometryA<T>>& multigeomA,
|
||||
const std::vector<GeometryB<T>>& multigeomB) {
|
||||
double d = std::numeric_limits<double>::infinity();
|
||||
for (const auto& geom : multigeomB)
|
||||
if (dist(geom, multigeomA) < d) d = dist(geom, multigeomA);
|
||||
return d;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double innerProd(double x1, double y1, double x2, double y2, double x3,
|
||||
double y3) {
|
||||
|
@ -788,7 +860,7 @@ inline double innerProd(double x1, double y1, double x2, double y2, double x3,
|
|||
double m13 = sqrt(dx31 * dx31 + dy31 * dy31);
|
||||
double theta = acos(std::min((dx21 * dx31 + dy21 * dy31) / (m12 * m13), 1.0));
|
||||
|
||||
return theta * (180 / M_PI);
|
||||
return theta * IRAD;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
@ -1240,6 +1312,56 @@ inline Box<T> getBoundingBox(const std::vector<Geometry<T>>& multigeo) {
|
|||
return b;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> getBoundingRect(const Box<T>& b) {
|
||||
auto box = Box<T>();
|
||||
auto centroid = util::geo::centroid(b);
|
||||
box = extendBox(b, box);
|
||||
box = extendBox(rotate(convexHull(b), 180, centroid), box);
|
||||
return box;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class Geometry, typename T>
|
||||
inline Box<T> getBoundingRect(const Geometry<T> geom) {
|
||||
return getBoundingRect<T>(getBoundingBox<T>(geom));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double getEnclosingRadius(const Point<T>& p, const Point<T>& pp) {
|
||||
return dist(p, pp);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double getEnclosingRadius(const Point<T>& p, const Line<T>& l) {
|
||||
double ret = 0;
|
||||
for (const auto& pp : l)
|
||||
if (getEnclosingRadius(p, pp) > ret) ret = getEnclosingRadius(p, pp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double getEnclosingRadius(const Point<T>& p, const Polygon<T>& pg) {
|
||||
double ret = 0;
|
||||
for (const auto& pp : pg.getOuter())
|
||||
if (getEnclosingRadius(p, pp) > ret) ret = getEnclosingRadius(p, pp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> class Geometry, typename T>
|
||||
inline double getEnclosingRadius(const Point<T>& p,
|
||||
const std::vector<Geometry<T>>& multigeom) {
|
||||
double ret = 0;
|
||||
for (const auto& pp : multigeom)
|
||||
if (getEnclosingRadius(p, pp) > ret) ret = getEnclosingRadius(p, pp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Polygon<T> convexHull(const Point<T>& p) {
|
||||
|
@ -1308,7 +1430,7 @@ inline Polygon<T> convexHull(const MultiPoint<T>& l) {
|
|||
convexHullImpl(l, 0, 1, &hull);
|
||||
hull.push_back(hull.front());
|
||||
convexHullImpl(l, hull.size() - 2, hull.size() - 1, &hull);
|
||||
hull.pop_back();
|
||||
hull.pop_back();
|
||||
|
||||
return Polygon<T>(hull);
|
||||
}
|
||||
|
@ -1355,6 +1477,181 @@ inline Box<T> extendBox(const std::vector<Geometry<T>>& multigeom, Box<T> b) {
|
|||
return b;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> pointAt(const Line<T> l, double at) {
|
||||
return pointAtDist(l, at * len(l));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> pointAt(const Line<T> l, double at, size_t* lastI, double* totPos) {
|
||||
return pointAtDist(l, at * len(l), lastI, totPos);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> pointAtDist(const Line<T> l, double atDist) {
|
||||
return pointAtDist(l, atDist, 0, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> pointAtDist(const Line<T> l, double atDist, size_t* lastI,
|
||||
double* totPos) {
|
||||
if (l.size() == 1) {
|
||||
if (lastI) *lastI = 0;
|
||||
if (totPos) *totPos = 0;
|
||||
return l[1];
|
||||
}
|
||||
|
||||
if (atDist > geo::len(l)) atDist = geo::len(l);
|
||||
if (atDist < 0) atDist = 0;
|
||||
|
||||
double dist = 0;
|
||||
|
||||
const Point<T>* last = &l[0];
|
||||
|
||||
for (size_t i = 1; i < l.size(); i++) {
|
||||
const Point<T>& cur = l[i];
|
||||
double d = geo::dist(*last, cur);
|
||||
dist += d;
|
||||
|
||||
if (dist > atDist) {
|
||||
double p = (d - (dist - atDist));
|
||||
if (lastI) *lastI = i - 1;
|
||||
if (totPos) *totPos = atDist / util::geo::len(l);
|
||||
return interpolate(*last, cur, p / dist);
|
||||
}
|
||||
|
||||
last = &l[i];
|
||||
}
|
||||
|
||||
if (lastI) *lastI = l.size() - 1;
|
||||
if (totPos) *totPos = 1;
|
||||
return l.back();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> interpolate(const Point<T>& a, const Point<T>& b, double d) {
|
||||
double n1 = b.getX() - a.getX();
|
||||
double n2 = b.getY() - a.getY();
|
||||
return Point<T>(a.getX() + (n1 * d), a.getY() + (n2 * d));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Line<T> orthoLineAtDist(const Line<T>& l, double d, double length) {
|
||||
Point<T> avgP = pointAtDist(l, d);
|
||||
|
||||
double angle = angBetween(pointAtDist(l, d - 5), pointAtDist(l, d + 5));
|
||||
|
||||
double angleX1 = avgP.getX() + cos(angle + M_PI / 2) * length / 2;
|
||||
double angleY1 = avgP.getY() + sin(angle + M_PI / 2) * length / 2;
|
||||
|
||||
double angleX2 = avgP.getX() + cos(angle + M_PI / 2) * -length / 2;
|
||||
double angleY2 = avgP.getY() + sin(angle + M_PI / 2) * -length / 2;
|
||||
|
||||
return Line<T>{Point<T>(angleX1, angleY1), Point<T>(angleX2, angleY2)};
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Line<T> segment(const Line<T>& line, double a, double b) {
|
||||
if (a > b) {
|
||||
double c = a;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
size_t startI, endI;
|
||||
auto start = pointAt(line, a, &startI, 0);
|
||||
auto end = pointAt(line, b, &endI, 0);
|
||||
|
||||
return segment(line, start, startI, end, endI);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Line<T> segment(const Line<T>& line, const Point<T>& start, size_t startI,
|
||||
const Point<T>& end, size_t endI) {
|
||||
Line<T> ret;
|
||||
ret.push_back(start);
|
||||
|
||||
if (startI + 1 <= endI) {
|
||||
ret.insert(ret.end(), line.begin() + startI + 1, line.begin() + endI + 1);
|
||||
}
|
||||
ret.push_back(end);
|
||||
|
||||
// find a more performant way to clear the result of above
|
||||
ret = util::geo::simplify(ret, 0);
|
||||
|
||||
assert(ret.size());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Line<T> average(const std::vector<const Line<T>*>& lines) {
|
||||
return average(lines, std::vector<double>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Line<T> average(const std::vector<const Line<T>*>& lines,
|
||||
const std::vector<double>& weights) {
|
||||
bool weighted = lines.size() == weights.size();
|
||||
double stepSize;
|
||||
|
||||
double longestLength =
|
||||
std::numeric_limits<double>::min(); // avoid recalc of length on each
|
||||
// comparision
|
||||
for (auto p : lines) {
|
||||
if (len(*p) > longestLength) {
|
||||
longestLength = len(*p);
|
||||
}
|
||||
}
|
||||
|
||||
Line<T> ret;
|
||||
double total = 0;
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (weighted) {
|
||||
total += weights[i];
|
||||
} else {
|
||||
total += 1;
|
||||
}
|
||||
}
|
||||
|
||||
stepSize = AVERAGING_STEP / longestLength;
|
||||
bool end = false;
|
||||
for (double a = 0; !end; a += stepSize) {
|
||||
if (a > 1) {
|
||||
a = 1;
|
||||
end = true;
|
||||
}
|
||||
double x = 0, y = 0;
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
auto pl = lines[i];
|
||||
Point<T> p = pointAt(*pl, a);
|
||||
if (weighted) {
|
||||
x += p.getX() * weights[i];
|
||||
y += p.getY() * weights[i];
|
||||
} else {
|
||||
x += p.getX();
|
||||
y += p.getY();
|
||||
}
|
||||
}
|
||||
ret.push_back(Point<T>(x / total, y / total));
|
||||
}
|
||||
|
||||
simplify(ret, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double area(const Point<T>& b) {
|
||||
|
@ -1510,28 +1807,30 @@ inline Line<T> densify(const Line<T>& l, double d) {
|
|||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double frechetDistC(size_t i, size_t j, const Line<T>& p,
|
||||
const Line<T>& q,
|
||||
std::vector<std::vector<double>>& ca) {
|
||||
const Line<T>& q, std::vector<float>& ca) {
|
||||
// based on Eiter / Mannila
|
||||
// http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf
|
||||
|
||||
if (ca[i][j] > -1)
|
||||
return ca[i][j];
|
||||
if (ca[i * q.size() + j] > -1)
|
||||
return ca[i * q.size() + j];
|
||||
else if (i == 0 && j == 0)
|
||||
ca[i][j] = dist(p[0], q[0]);
|
||||
ca[i * q.size() + j] = dist(p[0], q[0]);
|
||||
else if (i > 0 && j == 0)
|
||||
ca[i][j] = std::max(frechetDistC(i - 1, 0, p, q, ca), dist(p[i], q[0]));
|
||||
ca[i * q.size() + j] =
|
||||
std::max(frechetDistC(i - 1, 0, p, q, ca), dist(p[i], q[0]));
|
||||
else if (i == 0 && j > 0)
|
||||
ca[i][j] = std::max(frechetDistC(0, j - 1, p, q, ca), dist(p[0], q[j]));
|
||||
ca[i * q.size() + j] =
|
||||
std::max(frechetDistC(0, j - 1, p, q, ca), dist(p[0], q[j]));
|
||||
else if (i > 0 && j > 0)
|
||||
ca[i][j] = std::max(std::min(std::min(frechetDistC(i - 1, j, p, q, ca),
|
||||
frechetDistC(i - 1, j - 1, p, q, ca)),
|
||||
frechetDistC(i, j - 1, p, q, ca)),
|
||||
dist(p[i], q[j]));
|
||||
ca[i * q.size() + j] =
|
||||
std::max(std::min(std::min(frechetDistC(i - 1, j, p, q, ca),
|
||||
frechetDistC(i - 1, j - 1, p, q, ca)),
|
||||
frechetDistC(i, j - 1, p, q, ca)),
|
||||
dist(p[i], q[j]));
|
||||
else
|
||||
ca[i][j] = std::numeric_limits<double>::infinity();
|
||||
ca[i * q.size() + j] = std::numeric_limits<float>::infinity();
|
||||
|
||||
return ca[i][j];
|
||||
return ca[i * q.size() + j];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
@ -1543,8 +1842,7 @@ inline double frechetDist(const Line<T>& a, const Line<T>& b, double d) {
|
|||
auto p = densify(a, d);
|
||||
auto q = densify(b, d);
|
||||
|
||||
std::vector<std::vector<double>> ca(p.size(),
|
||||
std::vector<double>(q.size(), -1.0));
|
||||
std::vector<float> ca(p.size() * q.size(), -1.0);
|
||||
double fd = frechetDistC(p.size() - 1, q.size() - 1, p, q, ca);
|
||||
|
||||
return fd;
|
||||
|
@ -1556,24 +1854,28 @@ inline double accFrechetDistC(const Line<T>& a, const Line<T>& b, double d) {
|
|||
auto p = densify(a, d);
|
||||
auto q = densify(b, d);
|
||||
|
||||
std::vector<std::vector<double>> ca(p.size(),
|
||||
std::vector<double>(q.size(), 0));
|
||||
assert(p.size());
|
||||
assert(q.size());
|
||||
|
||||
std::vector<float> ca(p.size() * q.size(), 0);
|
||||
|
||||
for (size_t i = 0; i < p.size(); i++)
|
||||
ca[i][0] = std::numeric_limits<double>::infinity();
|
||||
ca[i * q.size() + 0] = std::numeric_limits<float>::infinity();
|
||||
for (size_t j = 0; j < q.size(); j++)
|
||||
ca[0][j] = std::numeric_limits<double>::infinity();
|
||||
ca[0][0] = 0;
|
||||
ca[j] = std::numeric_limits<float>::infinity();
|
||||
ca[0] = 0;
|
||||
|
||||
for (size_t i = 1; i < p.size(); i++) {
|
||||
for (size_t j = 1; j < q.size(); j++) {
|
||||
double d = util::geo::dist(p[i], q[j]) * util::geo::dist(p[i], p[i - 1]);
|
||||
ca[i][j] =
|
||||
d + std::min(ca[i - 1][j], std::min(ca[i][j - 1], ca[i - 1][j - 1]));
|
||||
float d = util::geo::dist(p[i], q[j]) * util::geo::dist(p[i], p[i - 1]);
|
||||
ca[i * q.size() + j] =
|
||||
d + std::min(ca[(i - 1) * q.size() + j],
|
||||
std::min(ca[i * q.size() + (j - 1)],
|
||||
ca[(i - 1) * q.size() + (j - 1)]));
|
||||
}
|
||||
}
|
||||
|
||||
return ca[p.size() - 1][q.size() - 1];
|
||||
return ca[p.size() * q.size() - 1];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
@ -1586,23 +1888,65 @@ inline Point<T> latLngToWebMerc(double lat, double lng) {
|
|||
return Point<T>(x, y);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
//TODO: rename to lngLat
|
||||
inline Point<T> latLngToWebMerc(Point<T> lngLat) {
|
||||
return latLngToWebMerc<T>(lngLat.getY(), lngLat.getX());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> webMercToLatLng(double x, double y) {
|
||||
double lat =
|
||||
(1.5707963267948966 - (2.0 * atan(exp(-y / 6378137.0)))) * (180.0 / M_PI);
|
||||
double lon = x / 111319.4907932735677;
|
||||
const double lat =
|
||||
(1.5707963267948966 - (2.0 * atan(exp(-y / 6378137.0)))) * IRAD;
|
||||
const double lon = x / 111319.4907932735677;
|
||||
return Point<T>(lon, lat);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double haversine(T lat1, T lon1, T lat2, T lon2) {
|
||||
lat1 *= RAD;
|
||||
lat2 *= RAD;
|
||||
|
||||
const double dLat = lat2 - lat1;
|
||||
const double dLon = (lon2 - lon1) * RAD;
|
||||
|
||||
const double sDLat = sin(dLat / 2);
|
||||
const double sDLon = sin(dLon / 2);
|
||||
|
||||
const double a = (sDLat * sDLat) + (sDLon * sDLon) * cos(lat1) * cos(lat2);
|
||||
return 6378137.0 * 2.0 * asin(sqrt(a));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double haversine(const Point<T>& a, const Point<T>& b) {
|
||||
return haversine(a.getY(), a.getX(), b.getY(), b.getX());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double webMercMeterDist(const Point<T>& a, const Point<T>& b) {
|
||||
const auto llA = webMercToLatLng<T>(a.getX(), a.getY());
|
||||
const auto llB = webMercToLatLng<T>(b.getX(), b.getY());
|
||||
return haversine(llA.getY(), llA.getX(), llB.getY(), llB.getX());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G1, typename G2>
|
||||
inline double webMercMeterDist(const G1& a, const G2& b) {
|
||||
// euclidean distance on web mercator is in meters on equator,
|
||||
// and proportional to cos(lat) in both y directions
|
||||
|
||||
double latA = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965;
|
||||
double latB = 2 * atan(exp(b.getY() / 6378137.0)) - 1.5707965;
|
||||
// this is just an approximation
|
||||
|
||||
auto pa = centroid(a);
|
||||
auto pb = centroid(b);
|
||||
|
||||
double latA = 2 * atan(exp(pa.getY() / 6378137.0)) - 1.5707965;
|
||||
double latB = 2 * atan(exp(pb.getY() / 6378137.0)) - 1.5707965;
|
||||
|
||||
return util::geo::dist(a, b) * cos((latA + latB) / 2.0);
|
||||
}
|
||||
|
@ -1624,7 +1968,17 @@ inline double webMercDistFactor(const G& a) {
|
|||
double lat = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965;
|
||||
return cos(lat);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G>
|
||||
inline double latLngDistFactor(const G& a) {
|
||||
// euclidean distance on web mercator is in meters on equator,
|
||||
// and proportional to cos(lat) in both y directions
|
||||
|
||||
return cos(a.getY() * RAD);
|
||||
}
|
||||
|
||||
} // namespace geo
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GEO_GEO_H_
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
#ifndef UTIL_GEO_GRID_H_
|
||||
#define UTIL_GEO_GRID_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
namespace util {
|
||||
|
@ -21,6 +21,39 @@ class GridException : public std::runtime_error {
|
|||
template <typename V, template <typename> class G, typename T>
|
||||
class Grid {
|
||||
public:
|
||||
Grid(const Grid<V, G, T>&) = delete;
|
||||
Grid(Grid<V, G, T>&& o)
|
||||
: _width(o._width),
|
||||
_height(o._height),
|
||||
_cellWidth(o._cellWidth),
|
||||
_cellHeight(o._cellHeight),
|
||||
_bb(o._bb),
|
||||
_xWidth(o._xWidth),
|
||||
_yHeight(o._yHeight),
|
||||
_hasValIdx(o._hasValIdx),
|
||||
_grid(o._grid),
|
||||
_index(o._index),
|
||||
_removed(o._removed) {
|
||||
o._grid = 0;
|
||||
}
|
||||
|
||||
Grid<V, G, T>& operator=(Grid<V, G, T>&& o) {
|
||||
_width = o._width;
|
||||
_height = o._height;
|
||||
_cellWidth = o._cellWidth;
|
||||
_cellHeight = o._cellHeight;
|
||||
_bb = o._bb;
|
||||
_xWidth = o._xWidth;
|
||||
_yHeight = o._yHeight;
|
||||
_hasValIdx = o._hasValIdx;
|
||||
_grid = o._grid;
|
||||
_index = std::move(o._index);
|
||||
_removed = std::move(o._removed);
|
||||
o._grid = 0;
|
||||
|
||||
return *this;
|
||||
};
|
||||
|
||||
// initialization of a point grid with cell width w and cell height h
|
||||
// that covers the area of bounding box bbox
|
||||
Grid(double w, double h, const Box<T>& bbox);
|
||||
|
@ -36,6 +69,14 @@ class Grid {
|
|||
// the empty grid
|
||||
Grid(bool buildValIdx);
|
||||
|
||||
~Grid() {
|
||||
if (!_grid) return;
|
||||
for (size_t i = 0; i < _xWidth; i++) {
|
||||
delete[] _grid[i];
|
||||
}
|
||||
delete[] _grid;
|
||||
}
|
||||
|
||||
// add object t to this grid
|
||||
void add(G<T> geom, V val);
|
||||
void add(size_t x, size_t y, V val);
|
||||
|
@ -55,6 +96,9 @@ class Grid {
|
|||
size_t getXWidth() const;
|
||||
size_t getYHeight() const;
|
||||
|
||||
size_t getCellXFromX(double lon) const;
|
||||
size_t getCellYFromY(double lat) const;
|
||||
|
||||
private:
|
||||
double _width;
|
||||
double _height;
|
||||
|
@ -64,21 +108,17 @@ class Grid {
|
|||
|
||||
Box<T> _bb;
|
||||
|
||||
size_t _counter;
|
||||
|
||||
size_t _xWidth;
|
||||
size_t _yHeight;
|
||||
|
||||
bool _hasValIdx;
|
||||
|
||||
std::vector<std::vector<std::set<V> > > _grid;
|
||||
// raw 2d array, less memory overhead
|
||||
std::set<V>** _grid;
|
||||
std::map<V, std::set<std::pair<size_t, size_t> > > _index;
|
||||
std::set<V> _removed;
|
||||
|
||||
Box<T> getBox(size_t x, size_t y) const;
|
||||
|
||||
size_t getCellXFromX(double lon) const;
|
||||
size_t getCellYFromY(double lat) const;
|
||||
};
|
||||
|
||||
#include "util/geo/Grid.tpp"
|
||||
|
|
|
@ -11,7 +11,8 @@ Grid<V, G, T>::Grid(bool bldIdx)
|
|||
_cellHeight(0),
|
||||
_xWidth(0),
|
||||
_yHeight(0),
|
||||
_hasValIdx(bldIdx) {}
|
||||
_hasValIdx(bldIdx),
|
||||
_grid(0) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> class G, typename T>
|
||||
|
@ -28,11 +29,10 @@ Grid<V, G, T>::Grid(double w, double h, const Box<T>& bbox, bool bValIdx)
|
|||
: _cellWidth(fabs(w)),
|
||||
_cellHeight(fabs(h)),
|
||||
_bb(bbox),
|
||||
_hasValIdx(bValIdx) {
|
||||
_width =
|
||||
bbox.getUpperRight().getX() - bbox.getLowerLeft().getX();
|
||||
_height =
|
||||
bbox.getUpperRight().getY() - bbox.getLowerLeft().getY();
|
||||
_hasValIdx(bValIdx),
|
||||
_grid(0) {
|
||||
_width = bbox.getUpperRight().getX() - bbox.getLowerLeft().getX();
|
||||
_height = bbox.getUpperRight().getY() - bbox.getLowerLeft().getY();
|
||||
|
||||
if (_width < 0 || _height < 0) {
|
||||
_width = 0;
|
||||
|
@ -46,11 +46,11 @@ Grid<V, G, T>::Grid(double w, double h, const Box<T>& bbox, bool bValIdx)
|
|||
_yHeight = ceil(_height / _cellHeight);
|
||||
|
||||
// resize rows
|
||||
_grid.resize(_xWidth);
|
||||
_grid = new std::set<V>*[_xWidth];
|
||||
|
||||
// resize columns
|
||||
for (size_t i = 0; i < _xWidth; i++) {
|
||||
_grid[i].resize(_yHeight);
|
||||
for (size_t x = 0; x < _xWidth; x++) {
|
||||
_grid[x] = new std::set<V>[_yHeight];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,8 @@ void Grid<V, G, T>::add(G<T> geom, V val) {
|
|||
size_t neX = getCellXFromX(box.getUpperRight().getX());
|
||||
size_t neY = getCellYFromY(box.getUpperRight().getY());
|
||||
|
||||
for (size_t x = swX; x <= neX && x < _grid.size(); x++) {
|
||||
for (size_t y = swY; y <= neY && y < _grid[x].size(); y++) {
|
||||
for (size_t x = swX; x <= neX && x < _xWidth; x++) {
|
||||
for (size_t y = swY; y <= neY && y < _yHeight; y++) {
|
||||
if (intersects(geom, getBox(x, y))) {
|
||||
add(x, y, val);
|
||||
}
|
||||
|
@ -97,24 +97,23 @@ void Grid<V, G, T>::get(const Box<T>& box, std::set<V>* s) const {
|
|||
template <typename V, template <typename> class G, typename T>
|
||||
void Grid<V, G, T>::get(const G<T>& geom, double d, std::set<V>* s) const {
|
||||
Box<T> a = getBoundingBox(geom);
|
||||
Box<T> b(Point<T>(a.getLowerLeft().getX() - d,
|
||||
a.getLowerLeft().getY() - d),
|
||||
Point<T>(a.getUpperRight().getX() + d,
|
||||
a.getUpperRight().getY() + d));
|
||||
Box<T> b(
|
||||
Point<T>(a.getLowerLeft().getX() - d, a.getLowerLeft().getY() - d),
|
||||
Point<T>(a.getUpperRight().getX() + d, a.getUpperRight().getY() + d));
|
||||
return get(b, s);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> class G, typename T>
|
||||
void Grid<V, G, T>::get(size_t x, size_t y, std::set<V>* s) const {
|
||||
if (_hasValIdx) {
|
||||
if (_hasValIdx || _removed.size() == 0) {
|
||||
s->insert(_grid[x][y].begin(), _grid[x][y].end());
|
||||
} else {
|
||||
// if we dont have a value index, we have a set of deleted nodes.
|
||||
// in this case, only insert if not deleted
|
||||
std::copy_if(_grid[x][y].begin(), _grid[x][y].end(),
|
||||
std::inserter(*s, s->end()),
|
||||
[&](const V& v) { return Grid<V, G, T>::_removed.count(v) == 0; });
|
||||
std::copy_if(
|
||||
_grid[x][y].begin(), _grid[x][y].end(), std::inserter(*s, s->end()),
|
||||
[&](const V& v) { return Grid<V, G, T>::_removed.count(v) == 0; });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace util {
|
|||
namespace geo {
|
||||
|
||||
static const double MAX_EQ_DISTANCE = 15;
|
||||
static const double AVERAGING_STEP = 20;
|
||||
|
||||
// legacy code, will be removed in the future
|
||||
|
||||
|
@ -61,11 +60,11 @@ class PolyLine {
|
|||
PolyLine& operator>>(const Point<T>& p);
|
||||
|
||||
void reverse();
|
||||
PolyLine getReversed() const;
|
||||
PolyLine reversed() const;
|
||||
|
||||
void offsetPerp(double units);
|
||||
|
||||
PolyLine getPerpOffsetted(double units) const;
|
||||
PolyLine offsetted(double units) const;
|
||||
|
||||
const Line<T>& getLine() const;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ void PolyLine<T>::reverse() {
|
|||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getReversed() const {
|
||||
PolyLine<T> PolyLine<T>::reversed() const {
|
||||
PolyLine ret = *this;
|
||||
ret.reverse();
|
||||
return ret;
|
||||
|
@ -52,7 +52,7 @@ const Line<T>& PolyLine<T>::getLine() const {
|
|||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getPerpOffsetted(double units) const {
|
||||
PolyLine<T> PolyLine<T>::offsetted(double units) const {
|
||||
PolyLine p = *this;
|
||||
p.offsetPerp(units);
|
||||
return p;
|
||||
|
@ -70,8 +70,6 @@ void PolyLine<T>::offsetPerp(double units) {
|
|||
|
||||
if (fabs(units) < 0.001) return;
|
||||
|
||||
assert(getLength() > 0);
|
||||
|
||||
if (_line.size() < 2) return;
|
||||
|
||||
Line<T> ret;
|
||||
|
@ -180,6 +178,7 @@ template <typename T>
|
|||
PolyLine<T> PolyLine<T>::getSegment(const LinePoint<T>& start,
|
||||
const LinePoint<T>& end) const {
|
||||
PolyLine ret;
|
||||
|
||||
ret << start.p;
|
||||
|
||||
if (start.lastIndex + 1 <= end.lastIndex) {
|
||||
|
@ -189,7 +188,9 @@ PolyLine<T> PolyLine<T>::getSegment(const LinePoint<T>& start,
|
|||
ret << end.p;
|
||||
|
||||
// find a more performant way to clear the result of above
|
||||
ret.simplify(0);
|
||||
// ret.simplify(0);
|
||||
|
||||
// assert(ret.getLine().size());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -197,9 +198,19 @@ PolyLine<T> PolyLine<T>::getSegment(const LinePoint<T>& start,
|
|||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
LinePoint<T> PolyLine<T>::getPointAtDist(double atDist) const {
|
||||
if (atDist > getLength()) atDist = getLength();
|
||||
double l = getLength();
|
||||
if (atDist > l) atDist = l;
|
||||
if (atDist < 0) atDist = 0;
|
||||
|
||||
// shortcuts
|
||||
if (atDist == 0) {
|
||||
return LinePoint<T>(0, 0, _line.front());
|
||||
}
|
||||
|
||||
if (atDist == l) {
|
||||
return LinePoint<T>(_line.size() - 1, 1, _line.back());
|
||||
}
|
||||
|
||||
double dist = 0;
|
||||
|
||||
if (_line.size() == 1) return LinePoint<T>(0, 0, _line[0]);
|
||||
|
@ -213,8 +224,7 @@ LinePoint<T> PolyLine<T>::getPointAtDist(double atDist) const {
|
|||
|
||||
if (dist > atDist) {
|
||||
double p = (d - (dist - atDist));
|
||||
return LinePoint<T>(i - 1, atDist / getLength(),
|
||||
interpolate(*last, cur, p));
|
||||
return LinePoint<T>(i - 1, atDist / l, interpolate(*last, cur, p));
|
||||
}
|
||||
|
||||
last = &_line[i];
|
||||
|
@ -270,8 +280,9 @@ PolyLine<T> PolyLine<T>::average(const std::vector<const PolyLine<T>*>& lines,
|
|||
|
||||
double longestLength = DBL_MIN; // avoid recalc of length on each comparision
|
||||
for (const PolyLine* p : lines) {
|
||||
if (p->getLength() > longestLength) {
|
||||
longestLength = p->getLength();
|
||||
double l = p->getLength();
|
||||
if (l > longestLength) {
|
||||
longestLength = l;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,11 +388,13 @@ LinePoint<T> PolyLine<T>::projectOnAfter(const Point<T>& p, size_t a) const {
|
|||
assert(a < _line.size());
|
||||
std::pair<size_t, double> bc = nearestSegmentAfter(p, a);
|
||||
|
||||
Point<T> ret = geo::projectOn(_line[bc.first], p, _line[bc.first + 1]);
|
||||
size_t next = bc.first + 1;
|
||||
if (next >= _line.size()) next = bc.first;
|
||||
|
||||
if (getLength() > 0) {
|
||||
bc.second += dist(_line[bc.first], ret) / getLength();
|
||||
}
|
||||
Point<T> ret = geo::projectOn(_line[bc.first], p, _line[next]);
|
||||
|
||||
double l = getLength();
|
||||
if (l > 0) bc.second += dist(_line[bc.first], ret) / l;
|
||||
|
||||
return LinePoint<T>(bc.first, bc.second, ret);
|
||||
}
|
||||
|
@ -475,7 +488,7 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
|
|||
*/
|
||||
double STEP_SIZE = 2;
|
||||
double MAX_SKIPS = 4;
|
||||
double MIN_SEG_LENGTH = 1; // dmax / 2; // make this configurable!
|
||||
double MIN_SEG_LENGTH = 0.1; // dmax / 2; // make this configurable!
|
||||
|
||||
SharedSegments<T> ret;
|
||||
|
||||
|
@ -505,7 +518,6 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
|
|||
LinePoint<T> curCmpPointer = pl.projectOn(curPointer);
|
||||
LinePoint<T> curBackProjectedPointer = projectOn(curCmpPointer.p);
|
||||
|
||||
|
||||
skips = 0;
|
||||
|
||||
if (in) {
|
||||
|
@ -536,7 +548,6 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
|
|||
curEndCand.totalPos * length) > MIN_SEG_LENGTH &&
|
||||
fabs(curStartCandCmp.totalPos * plLength -
|
||||
curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) {
|
||||
assert(curStartCand.totalPos < curEndCand.totalPos);
|
||||
ret.segments.push_back(
|
||||
SharedSegment<T>(std::pair<LinePoint<T>, LinePoint<T>>(
|
||||
curStartCand, curStartCandCmp),
|
||||
|
@ -573,7 +584,6 @@ SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
|
|||
MIN_SEG_LENGTH &&
|
||||
fabs(curStartCandCmp.totalPos * plLength -
|
||||
curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) {
|
||||
assert(curStartCand.totalPos < curEndCand.totalPos);
|
||||
ret.segments.push_back(SharedSegment<T>(
|
||||
std::pair<LinePoint<T>, LinePoint<T>>(curStartCand, curStartCandCmp),
|
||||
std::pair<LinePoint<T>, LinePoint<T>>(curEndCand, curEndCandCmp)));
|
||||
|
@ -661,7 +671,8 @@ std::pair<double, double> PolyLine<T>::getSlopeBetween(double ad,
|
|||
template <typename T>
|
||||
std::pair<double, double> PolyLine<T>::getSlopeBetweenDists(double ad,
|
||||
double bd) const {
|
||||
return getSlopeBetween(ad / getLength(), bd / getLength());
|
||||
double l = getLength();
|
||||
return getSlopeBetween(ad / l, bd / l);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
|
|
@ -21,9 +21,10 @@ class Polygon {
|
|||
Polygon(const Line<T>& l) : _outer(l) {}
|
||||
Polygon(const Box<T>& b)
|
||||
: _outer({b.getLowerLeft(),
|
||||
Point<T>(b.getUpperRight().getX(), b.getLowerLeft().getY()),
|
||||
Point<T>(b.getLowerLeft().getX(), b.getUpperRight().getY()),
|
||||
b.getUpperRight(),
|
||||
Point<T>(b.getLowerLeft().getX(), b.getUpperRight().getY())}) {}
|
||||
Point<T>(b.getUpperRight().getX(), b.getLowerLeft().getY()),
|
||||
b.getLowerLeft()}) {}
|
||||
|
||||
const Line<T>& getOuter() const { return _outer; }
|
||||
Line<T>& getOuter() { return _outer; }
|
||||
|
|
94
src/util/geo/QuadTree.h
Normal file
94
src/util/geo/QuadTree.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2020, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GEO_QUADTREE_H_
|
||||
#define UTIL_GEO_QUADTREE_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/geo/output/GeoJsonOutput.h"
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
|
||||
template <typename V, typename T>
|
||||
struct QuadValue {
|
||||
V val; // the actual value of this entry
|
||||
Point<T> point; // the value's position
|
||||
|
||||
int64_t nextValue; // index of the next quad value, -1 means no next value
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct QuadNode {
|
||||
int64_t numEls; // number of elements, -1 if this is not a leaf node
|
||||
int64_t childs; // for leafs, points to the first value contained. for
|
||||
// other nodes, points to the array block containing the
|
||||
// 4 childs
|
||||
Box<T> bbox;
|
||||
};
|
||||
|
||||
template <typename V, typename T>
|
||||
struct SplitFunc {
|
||||
virtual ~SplitFunc() = default;
|
||||
virtual bool operator()(const QuadNode<T>& nd,
|
||||
const QuadValue<V, T>& newVal) const = 0;
|
||||
};
|
||||
|
||||
template <typename V, typename T>
|
||||
struct CapaSplitFunc : SplitFunc<V, T> {
|
||||
CapaSplitFunc(size_t c) : _c(c) {}
|
||||
virtual bool operator()(const QuadNode<T>& nd,
|
||||
const QuadValue<V, T>& newVal) const {
|
||||
UNUSED(newVal);
|
||||
return static_cast<size_t>(nd.numEls) + 1 > _c;
|
||||
}
|
||||
size_t _c;
|
||||
};
|
||||
|
||||
// QuadTree for point data (and only point data)
|
||||
template <typename V, typename T>
|
||||
class QuadTree {
|
||||
public:
|
||||
// initialization of a quad tree with maximum depth d and maximum node // capacity c
|
||||
QuadTree(size_t d, size_t c, const Box<T>& bbox);
|
||||
|
||||
QuadTree(size_t d, const SplitFunc<V, T>& splitF, const Box<T>& bbox);
|
||||
|
||||
// insert into the tree
|
||||
void insert(const V& val, const Point<T>& point);
|
||||
|
||||
// insert into a specific node
|
||||
void insert(int64_t vid, int64_t nid, size_t d);
|
||||
|
||||
size_t size() const;
|
||||
|
||||
const std::vector<QuadNode<T>>& getNds() const;
|
||||
const QuadNode<T>& getNd(size_t nid) const;
|
||||
|
||||
// GeoJSON output
|
||||
void print(std::ostream& o) const;
|
||||
|
||||
private:
|
||||
size_t _maxDepth;
|
||||
std::vector<QuadValue<V, T>> _vals;
|
||||
std::vector<QuadNode<T>> _nds;
|
||||
|
||||
CapaSplitFunc<V, T> _capaFunc;
|
||||
|
||||
const SplitFunc<V, T>& _splFunc;
|
||||
|
||||
|
||||
// split a node
|
||||
void split(size_t nid, size_t d);
|
||||
};
|
||||
|
||||
#include "util/geo/QuadTree.tpp"
|
||||
|
||||
} // namespace geo
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GEO_QUADTREE_H_
|
117
src/util/geo/QuadTree.tpp
Normal file
117
src/util/geo/QuadTree.tpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
QuadTree<V, T>::QuadTree(size_t d, size_t c, const Box<T>& bbox)
|
||||
: _maxDepth(d), _capaFunc(c), _splFunc(_capaFunc) {
|
||||
_nds.push_back(QuadNode<T>{0, 0, bbox});
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
QuadTree<V, T>::QuadTree(size_t d, const SplitFunc<V, T>& splitF,
|
||||
const Box<T>& bbox)
|
||||
: _maxDepth(d), _capaFunc(0), _splFunc(splitF) {
|
||||
_nds.push_back(QuadNode<T>{0, 0, bbox});
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
void QuadTree<V, T>::insert(const V& val, const Point<T>& pos) {
|
||||
if (!intersects(pos, _nds[0].bbox)) return;
|
||||
int64_t valId = _vals.size();
|
||||
_vals.push_back(QuadValue<V, T>{val, pos, -1});
|
||||
_vals[valId].nextValue = -1;
|
||||
insert(valId, 0, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
void QuadTree<V, T>::insert(int64_t vid, int64_t nid, size_t d) {
|
||||
if (!intersects(_vals[vid].point, _nds[nid].bbox)) return;
|
||||
|
||||
if (d < _maxDepth && _nds[nid].numEls > -1 &&
|
||||
_splFunc(_nds[nid], _vals[vid])) {
|
||||
split(nid, d);
|
||||
}
|
||||
|
||||
if (_nds[nid].numEls == -1) {
|
||||
// insert into fitting subtree
|
||||
for (size_t i = 0; i < 4; i++) insert(vid, _nds[nid].childs + i, d + 1);
|
||||
} else {
|
||||
if (_nds[nid].numEls == 0) {
|
||||
_nds[nid].childs = vid;
|
||||
} else {
|
||||
_vals[vid].nextValue = _nds[nid].childs;
|
||||
_nds[nid].childs = vid;
|
||||
}
|
||||
_nds[nid].numEls++;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
void QuadTree<V, T>::split(size_t nid, size_t d) {
|
||||
const auto& box = _nds[nid].bbox;
|
||||
T w = (box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2);
|
||||
|
||||
int64_t curEl = _nds[nid].numEls > 0 ? _nds[nid].childs : -1;
|
||||
|
||||
_nds[nid].numEls = -1; // the node is now a leaf node
|
||||
_nds[nid].childs = _nds.size(); // the nodes quadrant block starts here
|
||||
|
||||
// box at 0, 0
|
||||
_nds.push_back(QuadNode<T>{
|
||||
0, 0,
|
||||
Box<T>(box.getLowerLeft(), Point<T>(box.getLowerLeft().getX() + w,
|
||||
box.getLowerLeft().getY() + w))});
|
||||
// box at 0, 1
|
||||
_nds.push_back(QuadNode<T>{
|
||||
0, 0,
|
||||
Box<T>(Point<T>(box.getLowerLeft().getX() + w, box.getLowerLeft().getY()),
|
||||
Point<T>(box.getUpperRight().getX(),
|
||||
box.getLowerLeft().getY() + w))});
|
||||
// box at 1,0
|
||||
_nds.push_back(QuadNode<T>{
|
||||
0, 0,
|
||||
Box<T>(Point<T>(box.getLowerLeft().getX(), box.getLowerLeft().getY() + w),
|
||||
Point<T>(box.getLowerLeft().getX() + w,
|
||||
box.getUpperRight().getY()))});
|
||||
// box at 1,1
|
||||
_nds.push_back(QuadNode<T>{0, 0,
|
||||
Box<T>(Point<T>(box.getLowerLeft().getX() + w,
|
||||
box.getLowerLeft().getY() + w),
|
||||
box.getUpperRight())});
|
||||
|
||||
while (curEl > -1) {
|
||||
_vals[curEl].nextValue = -1;
|
||||
insert(curEl, nid, d + 1);
|
||||
curEl = _vals[curEl].nextValue;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
size_t QuadTree<V, T>::size() const {
|
||||
return _vals.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
const QuadNode<T>& QuadTree<V, T>::getNd(size_t nid) const {
|
||||
return _nds[nid];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
const std::vector<QuadNode<T>>& QuadTree<V, T>::getNds() const {
|
||||
return _nds;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, typename T>
|
||||
void QuadTree<V, T>::print(std::ostream& o) const {
|
||||
util::geo::output::GeoJsonOutput out(o);
|
||||
for (const auto& nd : _nds) {
|
||||
if (nd.numEls == -1) continue; // don't print non-leaf nodes
|
||||
out.print(util::geo::convexHull(nd.bbox), json::Dict{{"elements", json::Int(nd.numEls)}});
|
||||
}
|
||||
}
|
|
@ -19,13 +19,22 @@ class GeoGraphJsonOutput {
|
|||
public:
|
||||
inline GeoGraphJsonOutput(){};
|
||||
|
||||
// print a graph to the provided path
|
||||
// print a graph to the provided path, with optional JSON attributes
|
||||
// written on the graph-level
|
||||
template <typename N, typename E>
|
||||
void print(const util::graph::Graph<N, E>& outG, std::ostream& str);
|
||||
template <typename N, typename E>
|
||||
void print(const util::graph::Graph<N, E>& outG, std::ostream& str,
|
||||
json::Val attrs);
|
||||
|
||||
// print a graph to the provided path, but treat coordinates as Web Mercator coordinates and reproject to WGS84
|
||||
// print a graph to the provided path, but treat coordinates as Web Mercator
|
||||
// coordinates and reproject to WGS84, with optional JSON attributes
|
||||
// written on the graph-level
|
||||
template <typename N, typename E>
|
||||
void printLatLng(const util::graph::Graph<N, E>& outG, std::ostream& str);
|
||||
template <typename N, typename E>
|
||||
void printLatLng(const util::graph::Graph<N, E>& outG, std::ostream& str,
|
||||
json::Val attrs);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
|
@ -34,12 +43,13 @@ class GeoGraphJsonOutput {
|
|||
|
||||
// print a graph to the provided path
|
||||
template <typename N, typename E>
|
||||
void printImpl(const util::graph::Graph<N, E>& outG, std::ostream& str, bool proj);
|
||||
void printImpl(const util::graph::Graph<N, E>& outG, std::ostream& str,
|
||||
bool proj, json::Val attrs);
|
||||
};
|
||||
|
||||
#include "util/geo/output/GeoGraphJsonOutput.tpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace output
|
||||
} // namespace geo
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_
|
||||
|
|
|
@ -16,21 +16,36 @@ Line<T> GeoGraphJsonOutput::createLine(const util::geo::Point<T>& a,
|
|||
template <typename N, typename E>
|
||||
void GeoGraphJsonOutput::print(const util::graph::Graph<N, E>& outG,
|
||||
std::ostream& str) {
|
||||
printImpl(outG, str, false);
|
||||
printImpl(outG, str, false, {});
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void GeoGraphJsonOutput::printLatLng(const util::graph::Graph<N, E>& outG,
|
||||
std::ostream& str) {
|
||||
printImpl(outG, str, true);
|
||||
printImpl(outG, str, true, {});
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void GeoGraphJsonOutput::print(const util::graph::Graph<N, E>& outG,
|
||||
std::ostream& str, json::Val attrs) {
|
||||
printImpl(outG, str, false, attrs);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void GeoGraphJsonOutput::printLatLng(const util::graph::Graph<N, E>& outG,
|
||||
std::ostream& str, json::Val attrs) {
|
||||
printImpl(outG, str, true, attrs);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void GeoGraphJsonOutput::printImpl(const util::graph::Graph<N, E>& outG,
|
||||
std::ostream& str, bool proj) {
|
||||
GeoJsonOutput _out(str);
|
||||
std::ostream& str, bool proj, json::Val attrs) {
|
||||
|
||||
GeoJsonOutput _out(str, attrs);
|
||||
|
||||
// first pass, nodes
|
||||
for (util::graph::Node<N, E>* n : outG.getNds()) {
|
||||
|
|
|
@ -28,12 +28,30 @@ class GeoJsonOutput {
|
|||
template <typename T>
|
||||
void print(const Line<T>& l, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void print(const MultiLine<T>& l, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void print(const Polygon<T>& l, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void print(const MultiPolygon<T>& l, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void printLatLng(const Point<T>& p, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void printLatLng(const Line<T>& l, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void printLatLng(const MultiLine<T>& l, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void printLatLng(const Polygon<T>& l, json::Val attrs);
|
||||
|
||||
template <typename T>
|
||||
void printLatLng(const MultiPolygon<T>& l, json::Val attrs);
|
||||
|
||||
void flush();
|
||||
|
||||
private:
|
||||
|
@ -41,8 +59,8 @@ class GeoJsonOutput {
|
|||
};
|
||||
|
||||
#include "util/geo/output/GeoJsonOutput.tpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace output
|
||||
} // namespace geo
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_
|
||||
|
|
|
@ -47,6 +47,46 @@ void GeoJsonOutput::print(const Line<T>& line, json::Val attrs) {
|
|||
_wr.close();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::print(const MultiLine<T>& line, json::Val attrs) {
|
||||
for (const auto& l : line) print(l, attrs);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::print(const Polygon<T>& poly, json::Val attrs) {
|
||||
if (!poly.getOuter().size()) return;
|
||||
_wr.obj();
|
||||
_wr.keyVal("type", "Feature");
|
||||
|
||||
_wr.key("geometry");
|
||||
_wr.obj();
|
||||
_wr.keyVal("type", "Polygon");
|
||||
_wr.key("coordinates");
|
||||
_wr.arr();
|
||||
_wr.arr();
|
||||
for (auto p : poly.getOuter()) {
|
||||
_wr.arr();
|
||||
_wr.val(p.getX());
|
||||
_wr.val(p.getY());
|
||||
_wr.close();
|
||||
}
|
||||
|
||||
_wr.close();
|
||||
_wr.close();
|
||||
_wr.close();
|
||||
_wr.key("properties");
|
||||
_wr.val(attrs);
|
||||
_wr.close();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::print(const MultiPolygon<T>& mpoly, json::Val attrs) {
|
||||
for (const auto& p : mpoly) print(p, attrs);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::printLatLng(const Point<T>& p, json::Val attrs) {
|
||||
|
@ -58,7 +98,30 @@ void GeoJsonOutput::printLatLng(const Point<T>& p, json::Val attrs) {
|
|||
template <typename T>
|
||||
void GeoJsonOutput::printLatLng(const Line<T>& line, json::Val attrs) {
|
||||
Line<T> projL;
|
||||
for (auto p : line) projL.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
|
||||
for (auto p : line)
|
||||
projL.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
|
||||
|
||||
print(projL, attrs);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::printLatLng(const MultiLine<T>& mline, json::Val attrs) {
|
||||
for (const auto& l : mline) printLatLng(l, attrs);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::printLatLng(const Polygon<T>& poly, json::Val attrs) {
|
||||
Polygon<T> projP;
|
||||
for (auto p : poly)
|
||||
projP.push_back(util::geo::webMercToLatLng<double>(p.getX(), p.getY()));
|
||||
|
||||
print(projP, attrs);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::printLatLng(const MultiPolygon<T>& mpoly, json::Val attrs) {
|
||||
for (const auto& p : mpoly) printLatLng(p, attrs);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#include <stack>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
#include "util/graph/Node.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
@ -20,13 +20,26 @@ using util::graph::Edge;
|
|||
// collection of general graph algorithms
|
||||
class Algorithm {
|
||||
public:
|
||||
template <typename N, typename E>
|
||||
struct EdgeCheckFunc {
|
||||
virtual bool operator()(const Node<N, E>* frNd,
|
||||
const Edge<N, E>* edge) const {
|
||||
UNUSED(frNd);
|
||||
UNUSED(edge);
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E>
|
||||
static std::vector<std::set<Node<N, E>*> > connectedComponents(
|
||||
const UndirGraph<N, E>& g);
|
||||
|
||||
template <typename N, typename E>
|
||||
static std::vector<std::set<Node<N, E>*> > connectedComponents(
|
||||
const UndirGraph<N, E>& g, const EdgeCheckFunc<N, E>& checkFunc);
|
||||
};
|
||||
|
||||
#include "util/graph/Algorithm.tpp"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
//
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
std::vector<std::set<Node<N, E>*>> Algorithm::connectedComponents(
|
||||
const UndirGraph<N, E>& g) {
|
||||
return connectedComponents(g, EdgeCheckFunc<N, E>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
std::vector<std::set<Node<N, E>*> > Algorithm::connectedComponents(
|
||||
const UndirGraph<N, E>& g) {
|
||||
std::vector<std::set<Node<N, E>*>> Algorithm::connectedComponents(
|
||||
const UndirGraph<N, E>& g, const EdgeCheckFunc<N, E>& checkFunc) {
|
||||
std::vector<std::set<Node<N, E>*>> ret;
|
||||
std::set<Node<N, E>*> visited;
|
||||
|
||||
|
@ -22,6 +29,7 @@ std::vector<std::set<Node<N, E>*> > Algorithm::connectedComponents(
|
|||
visited.insert(cur);
|
||||
|
||||
for (auto* e : cur->getAdjList()) {
|
||||
if (!checkFunc(cur, e)) continue;
|
||||
if (!visited.count(e->getOtherNd(cur))) q.push(e->getOtherNd(cur));
|
||||
}
|
||||
}
|
||||
|
|
7
src/util/graph/BiDijkstra.cpp
Normal file
7
src/util/graph/BiDijkstra.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "util/graph/BiDijkstra.h"
|
||||
|
||||
size_t util::graph::BiDijkstra::ITERS = 0;
|
129
src/util/graph/BiDijkstra.h
Normal file
129
src/util/graph/BiDijkstra.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_BIDIJKSTRA_H_
|
||||
#define UTIL_GRAPH_BIDIJKSTRA_H_
|
||||
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Node.h"
|
||||
#include "util/graph/ShortestPath.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Edge;
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
|
||||
// bidirectional dijkstras algorithm for util graph
|
||||
class BiDijkstra : public ShortestPath<BiDijkstra> {
|
||||
public:
|
||||
template <typename N, typename E, typename C>
|
||||
struct RouteNode {
|
||||
RouteNode() : n(0), parent(0), d(), h() {}
|
||||
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h() {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d)
|
||||
: n(n), parent(parent), d(d), h() {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h)
|
||||
: n(n), parent(parent), d(d), h(h) {}
|
||||
|
||||
Node<N, E>* n;
|
||||
Node<N, E>* parent;
|
||||
|
||||
// the cost so far
|
||||
C d;
|
||||
|
||||
// the heuristical remaining cost + the cost so far
|
||||
C h;
|
||||
|
||||
bool operator<(const RouteNode<N, E, C>& p) const { return h > p.h; }
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using Settled = std::unordered_map<Node<N, E>*, RouteNode<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using PQ = std::priority_queue<RouteNode<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc : public util::graph::CostFunc<N, E, C> {
|
||||
virtual ~CostFunc() = default; C operator()(const Edge<N, E>* from, const Node<N, E>* n,
|
||||
const Edge<N, E>* to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(n);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc : public util::graph::HeurFunc<N, E, C> {
|
||||
virtual ~HeurFunc() = default;
|
||||
C operator()(const Edge<N, E>* from,
|
||||
const std::set<Edge<N, E>*>& to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>&,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNode);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C relaxFwd(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledBwd);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C relaxBwd(const std::set<Node<N, E>*>& froms, RouteNode<N, E, C>& cur,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledFwd);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void buildPath(Node<N, E>* curN, Settled<N, E, C>& settledFwd,
|
||||
Settled<N, E, C>& settledBwd, NList<N, E>* resNodes,
|
||||
EList<N, E>* resEdges);
|
||||
|
||||
static size_t ITERS;
|
||||
};
|
||||
|
||||
#include "util/graph/BiDijkstra.tpp"
|
||||
} // namespace graph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GRAPH_BIDIJKSTRA_H_
|
293
src/util/graph/BiDijkstra.tpp
Normal file
293
src/util/graph/BiDijkstra.tpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::shortestPathImpl(Node<N, E>* from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (from->getOutDeg() == 0) return costFunc.inf();
|
||||
|
||||
std::set<Node<N, E>*> froms;
|
||||
froms.insert(from);
|
||||
|
||||
return shortestPathImpl(froms, to, costFunc, heurFunc, resEdges, resNodes);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
Settled<N, E, C> settledFwd, settledBwd;
|
||||
PQ<N, E, C> pqFwd, pqBwd;
|
||||
bool found = false;
|
||||
|
||||
// starter for forward search
|
||||
for (auto n : from) pqFwd.emplace(n);
|
||||
|
||||
auto l = costFunc.inf();
|
||||
|
||||
// starter for backward search
|
||||
for (auto n : to) pqBwd.emplace(n);
|
||||
|
||||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pqFwd.empty() && !pqBwd.empty()) {
|
||||
if (costFunc.inf() <= pqFwd.top().h && costFunc.inf() <= pqBwd.top().h)
|
||||
return costFunc.inf();
|
||||
|
||||
if (pqFwd.top() < pqBwd.top()) {
|
||||
auto se = settledBwd.find(pqBwd.top().n);
|
||||
if (se != settledBwd.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pqBwd.top().d) {
|
||||
pqBwd.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto se = settledFwd.find(pqFwd.top().n);
|
||||
if (se != settledFwd.end()) {
|
||||
// to allow non-consistent heuristics
|
||||
if (se->second.d <= pqFwd.top().d) {
|
||||
pqFwd.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BiDijkstra::ITERS++;
|
||||
|
||||
if (pqFwd.top() < pqBwd.top()) {
|
||||
cur = pqBwd.top();
|
||||
pqBwd.pop();
|
||||
settledBwd[cur.n] = cur;
|
||||
if (settledFwd.find(cur.n) != settledFwd.end()) {
|
||||
auto newL = cur.d + settledFwd.find(cur.n)->second.d;
|
||||
|
||||
if (!(newL > l)) {
|
||||
l = newL;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
C bestCost = relaxBwd(from, cur, costFunc, heurFunc, pqBwd, settledFwd);
|
||||
if (bestCost < l) l = bestCost;
|
||||
} else {
|
||||
cur = pqFwd.top();
|
||||
pqFwd.pop();
|
||||
settledFwd[cur.n] = cur;
|
||||
if (settledBwd.find(cur.n) != settledBwd.end()) {
|
||||
auto newL = cur.d + settledBwd.find(cur.n)->second.d;
|
||||
|
||||
if (!(newL > l)) {
|
||||
l = newL;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
C bestCost = relaxFwd(cur, to, costFunc, heurFunc, pqFwd, settledBwd);
|
||||
if (bestCost < l) l = bestCost;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) return costFunc.inf();
|
||||
|
||||
buildPath(cur.n, settledFwd, settledBwd, resNodes, resEdges);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Node<N, E>*, C> BiDijkstra::shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
UNUSED(from);
|
||||
UNUSED(to);
|
||||
UNUSED(costFunc);
|
||||
UNUSED(heurFunc);
|
||||
UNUSED(resEdges);
|
||||
UNUSED(resNodes);
|
||||
assert(false);
|
||||
// std::unordered_map<Node<N, E>*, C> costs;
|
||||
// if (to.size() == 0) return costs;
|
||||
// // init costs with inf
|
||||
// for (auto n : to) costs[n] = costFunc.inf();
|
||||
|
||||
// if (from->getOutDeg() == 0) return costs;
|
||||
// Settled<N, E, C> settled;
|
||||
// PQ<N, E, C> pq;
|
||||
|
||||
// size_t found = 0;
|
||||
|
||||
// pq.emplace(from);
|
||||
// RouteNode<N, E, C> cur;
|
||||
|
||||
// while (!pq.empty()) {
|
||||
// if (costFunc.inf() <= pq.top().h) return costs;
|
||||
// if (settled.find(pq.top().n) != settled.end()) {
|
||||
// pq.pop();
|
||||
// continue;
|
||||
// }
|
||||
// BiDijkstra::ITERS++;
|
||||
|
||||
// cur = pq.top();
|
||||
// pq.pop();
|
||||
|
||||
// settled[cur.n] = cur;
|
||||
|
||||
// if (to.find(cur.n) != to.end()) {
|
||||
// found++;
|
||||
// }
|
||||
|
||||
// if (found == to.size()) break;
|
||||
|
||||
// relax(cur, to, costFunc, heurFunc, pq);
|
||||
// }
|
||||
|
||||
// for (auto nto : to) {
|
||||
// if (!settled.count(nto)) continue;
|
||||
// Node<N, E>* curN = nto;
|
||||
// costs[nto] = settled[curN].d;
|
||||
|
||||
// buildPath(nto, settled, resNodes[nto], resEdges[nto]);
|
||||
// }
|
||||
|
||||
// return costs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void BiDijkstra::relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
for (auto edge : cur.n->getAdjListOut()) {
|
||||
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
// addition done here to avoid it in the PQ
|
||||
const C& newH = newC + heurFunc(edge->getOtherNd(cur.n), to);
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::relaxFwd(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledBwd) {
|
||||
UNUSED(to);
|
||||
UNUSED(heurFunc);
|
||||
C ret = costFunc.inf();
|
||||
for (auto edge : cur.n->getAdjListOut()) {
|
||||
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
// addition done here to avoid it in the PQ
|
||||
// const C& newH = newC + heurFunc(froms, edge->getOtherNd(cur.n));
|
||||
|
||||
// TODO:
|
||||
const C& newH = newC + 0;
|
||||
|
||||
// update new best found cost
|
||||
if (settledBwd.find(edge->getOtherNd(cur.n)) != settledBwd.end()) {
|
||||
C bwdCost = settledBwd.find(edge->getOtherNd(cur.n))->second.d + newC;
|
||||
if (bwdCost < ret) ret = bwdCost;
|
||||
}
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C BiDijkstra::relaxBwd(const std::set<Node<N, E>*>& froms,
|
||||
RouteNode<N, E, C>& cur,
|
||||
const util::graph::CostFunc<N, E, C>& costFunc,
|
||||
const util::graph::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq, const Settled<N, E, C>& settledFwd) {
|
||||
UNUSED(froms);
|
||||
UNUSED(heurFunc);
|
||||
C ret = costFunc.inf();
|
||||
for (auto edge : cur.n->getAdjListIn()) {
|
||||
C newC = costFunc(edge->getOtherNd(cur.n), edge, cur.n);
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
// addition done here to avoid it in the PQ
|
||||
// const C& newH = newC + heurFunc(froms, edge->getOtherNd(cur.n));
|
||||
|
||||
// TODO:
|
||||
const C& newH = newC + 0;
|
||||
|
||||
// update new best found cost
|
||||
if (settledFwd.find(edge->getOtherNd(cur.n)) != settledFwd.end()) {
|
||||
C fwdCost = settledFwd.find(edge->getOtherNd(cur.n))->second.d + newC;
|
||||
if (fwdCost < ret) ret = fwdCost;
|
||||
}
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void BiDijkstra::buildPath(Node<N, E>* curN, Settled<N, E, C>& settledFwd,
|
||||
Settled<N, E, C>& settledBwd, NList<N, E>* resNodes,
|
||||
EList<N, E>* resEdges) {
|
||||
Node<N, E>* curNFwd = curN;
|
||||
Node<N, E>* curNBwd = curN;
|
||||
|
||||
// the forward part
|
||||
while (resNodes || resEdges) {
|
||||
const RouteNode<N, E, C>& curNode = settledFwd[curNFwd];
|
||||
if (resNodes) resNodes->push_back(curNode.n);
|
||||
if (!curNode.parent) break;
|
||||
|
||||
if (resEdges) {
|
||||
for (auto e : curNode.n->getAdjListIn()) {
|
||||
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
|
||||
}
|
||||
}
|
||||
curNFwd = curNode.parent;
|
||||
}
|
||||
|
||||
if (resNodes) std::reverse(resNodes->begin(), resNodes->end());
|
||||
if (resEdges) std::reverse(resEdges->begin(), resEdges->end());
|
||||
|
||||
// the backward part
|
||||
while (resNodes || resEdges) {
|
||||
const RouteNode<N, E, C>& curNode = settledBwd[curNBwd];
|
||||
if (resNodes && curNode.n != curN) resNodes->push_back(curNode.n);
|
||||
if (!curNode.parent) break;
|
||||
|
||||
if (resEdges) {
|
||||
for (auto e : curNode.n->getAdjListOut()) {
|
||||
if (e->getOtherNd(curNode.n) == curNode.parent) resEdges->push_back(e);
|
||||
}
|
||||
}
|
||||
curNBwd = curNode.parent;
|
||||
}
|
||||
|
||||
if (resNodes) std::reverse(resNodes->begin(), resNodes->end());
|
||||
if (resEdges) std::reverse(resEdges->begin(), resEdges->end());
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue