clean up and refactor half-baked development commits and squash them into a new version.

Changes:

* support for multiple GTFS feeds as input in filtering, read default global and local configuration files
* be a bit more memory conservative
* make caching optional
* dont delete orphan edge if it would transform a degree 3 node with a possible full turn into a degree 2 node eligible for contraction
* dedicated filters for funicular and gondola
* make max snap level option more intuitive
* allow filter rules for level 0
* additional fallback for station snapping
* dont try to route for MOT unequal to trip in -T mode, force-snap to orphaned OSM station if not snap was possible
* write bounds to filtered osm
* remove unused surrounding heuristic
* use bus lanes info
* be a bit more tolerant for bus oneway streets
* create missing directories
* error if no cfg is present, clean up evaluation Makefile
This commit is contained in:
Patrick Brosi 2019-01-10 16:52:59 +01:00
parent 2cc2d2dc23
commit 63f0b61ea1
60 changed files with 4532 additions and 1576 deletions

View file

@ -14,11 +14,12 @@ set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build")
find_package(OpenMP) find_package(OpenMP)
if(OPENMP_FOUND) if (OPENMP_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif() endif()
# set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake # set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake
if(OPENMP_FOUND) 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") 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")
@ -71,18 +72,17 @@ add_test("utilTest" utilTest)
# custom eval target # custom eval target
add_custom_target( add_custom_target(
eval eval
COMMAND make COMMAND make
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval
) )
# handles install target # handles install target
install( install(
FILES pfaedle.cfg DESTINATION etc/${PROJECT_NAME} COMPONENT config PERMISSIONS WORLD_READ FILES pfaedle.cfg DESTINATION etc/${PROJECT_NAME} COMPONENT config PERMISSIONS WORLD_READ
) )
install( install(
FILES build/pfaedle DESTINATION bin FILES build/pfaedle DESTINATION bin
PERMISSIONS WORLD_EXECUTE COMPONENT binaries PERMISSIONS WORLD_EXECUTE COMPONENT binaries
) )

View file

@ -40,7 +40,7 @@ make install
## Generating shapes for a GTFS feed ## Generating shapes for a GTFS feed
``` ```
pfaedle -c <CFG FILE> -x <OSM FILE> <GTFS INPUT FOLDER> pfaedle -x <OSM FILE> <GTFS INPUT FOLDER>
``` ```
A shape'd version of the input GTFS feed will be written to `./gtfs-out`. A shape'd version of the input GTFS feed will be written to `./gtfs-out`.
@ -51,22 +51,15 @@ input feed. To drop all existing shapes, use the `-D` flag.
For example, you may generate (and replace existing, see -D parameter) shapes for the GTFS dataset for Freiburg like this: For example, you may generate (and replace existing, see -D parameter) shapes for the GTFS dataset for Freiburg like this:
``` ```
$ mkdir freiburg_gtfs && cd freiburg_gtfs $ wget https://fritz.freiburg.de/csv_Downloads/VAGFR.zip && unzip VAGFR.zip
$ wget https://fritz.freiburg.de/csv_Downloads/VAGFR.zip $ wget http://download.geofabrik.de/europe/germany/baden-wuerttemberg/freiburg-regbez-latest.osm.bz2 && bunzip2 freiburg-regbez-latest.osm.bz2
$ unzip VAGFR.zip $ pfaedle -D -x freiburg-regbez-latest.osm .
$ wget http://download.geofabrik.de/europe/germany/baden-wuerttemberg/freiburg-regbez-latest.osm.bz2
$ bunzip2 freiburg-regbez-latest.osm.bz2
$ mkdir gtfs-out
$ pfaedle -D -c pfaedle.cfg -x freiburg-regbez-latest.osm .
``` ```
A default configuration file `pfaedle.cfg` can be found in this repo.
## Generating shapes for a specific MOT ## Generating shapes for a specific MOT
To generate shapes only for a specific mot, use the `-m` option. Possible To generate shapes only for a specific mot, use the `-m` option. Possible
values are either `tram`, `bus`, `rail`, `subway`, `ferry`, `funicular`, values are either `tram`, `bus`, `coach`, `rail`, `subway`, `ferry`, `funicular`,
`gondola`, `all` (default) or GTFS vehicle type codes (0, 1, 2, 3, 4, 5, 6, 7). `gondola`, `all` (default) or GTFS vehicle type codes (0, 1, 2, 3, 4, 5, 6, 7).
Multiple values can be specified (comma separated). Multiple values can be specified (comma separated).
@ -75,7 +68,7 @@ Multiple values can be specified (comma separated).
`pfaedle` comes with the ability to filter OpenStreetMap data. If you specify `pfaedle` comes with the ability to filter OpenStreetMap data. If you specify
the `-X` flag, `pfaedle` will filter the input OSM file and output a new OSM the `-X` flag, `pfaedle` will filter the input OSM file and output a new OSM
file which contains *exactly* the data needed to calculate the shapes for the file which contains exactly the data needed to calculate the shapes for the
input GTFS feed and the input configuration. input GTFS feed and the input configuration.
This can be used to avoid parsing (for example) the entire world.osm on each This can be used to avoid parsing (for example) the entire world.osm on each
@ -95,9 +88,7 @@ The following flags may be useful for debugging:
# Configuration # Configuration
The main config file distributed with this repository is `pfaedle.cfg`. The 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.
config file has some comments which hopefully explain the meaning behind the
parameters.
# Evaluation # Evaluation

View file

@ -7,111 +7,127 @@ lighteval: vitoria.lighteval stuttgart.lighteval paris.lighteval switzerland.lig
eval: vitoria.eval stuttgart.eval paris.eval switzerland.eval eval: vitoria.eval stuttgart.eval paris.eval switzerland.eval
clean: clean:
rm -f *.eval @rm -f *.eval
rm -rf gtfs @rm -rf gtfs
rf -rf osm @rm -rf osm
rm -rf evalout @rm -rf evalout
osmconvert: osmconvert:
wget -O - http://m.m.i24.cc/osmconvert.c | cc -x c - -lz -O3 -o 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 %.lighteval: osm/%.osm gtfs/%/stops.txt gtfs/%/stop_times.txt gtfs/%/trips.txt gtfs/%/routes.txt eval.cfg
mkdir -p gtfs/$*/shaped @echo `date +"[%F %T.%3N]"` "EVAL : Running light (without stats) evaluation for '"$*"'..."
rm -f gtfs/$*/shaped/* @mkdir -p gtfs/$*/shaped
../build/pfaedle -x $< -i gtfs/$* -c eval.cfg -o gtfs/$*/shaped -D -m all 2>&1 | tee $@ @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 %.eval: osm/%.osm gtfs/%/stops.txt gtfs/%/stop_times.txt gtfs/%/trips.txt gtfs/%/routes.txt eval.cfg eval-wo-osm-line-rels.cfg
mkdir -p gtfs/$*/shaped @echo `date +"[%F %T.%3N]"` "EVAL : Running evaluation for '"$*"'..."
rm -f gtfs/$*/shaped/* @mkdir -p gtfs/$*/shaped
mkdir -p evalout/ @rm -f gtfs/$*/shaped/*
mkdir -p evalout/$*/ @mkdir -p evalout/
mkdir -p evalout/$*/hmm+osm @mkdir -p evalout/$*/
../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 $@ @mkdir -p evalout/$*/hmm+osm
find evalout/$*/hmm+osm/ -name "*.json" -print0 | xargs -0 rm @../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 @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 $@ @../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 @find evalout/$*/greedy/ -name "*.json" -print0 | xargs -0 rm
mkdir -p evalout/$*/greedy2 @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 $@ @../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 @find evalout/$*/greedy2/ -name "*.json" -print0 | xargs -0 rm
mkdir -p evalout/$*/hmm @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 $@ @../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 @find evalout/$*/hmm/ -name "*.json" -print0 | xargs -0 rm
osm/spain-latest.osm.pbf: osm/spain-latest.osm.pbf:
mkdir -p osm @mkdir -p osm
wget http://download.geofabrik.de/europe/spain-latest.osm.pbf -O $@ @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 osm/spain-latest.osm: osm/spain-latest.osm.pbf osmconvert
@# pre-filter to vitoria gasteiz @# pre-filter to vitoria gasteiz
osmconvert -b=-2.8661,42.7480,-2.4788,43.0237 $< > $@ @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: osm/baden-wuerttemberg-latest.osm.pbf:
mkdir -p osm @mkdir -p osm
wget http://download.geofabrik.de/europe/germany/baden-wuerttemberg-latest.osm.pbf -O $@ @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 osm/baden-wuerttemberg-latest.osm: osm/baden-wuerttemberg-latest.osm.pbf osmconvert
osmconvert $< > $@ @echo `date +"[%F %T.%3N]"` "EVAL : Extracting OSM data..."
@osmconvert $< > $@
osm/france-latest.osm.pbf: osm/france-latest.osm.pbf:
mkdir -p osm @mkdir -p osm
wget http://download.geofabrik.de/europe/france-latest.osm.pbf -O $@ @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 osm/paris-latest.osm: osm/france-latest.osm.pbf osmconvert
@# pre-filter to greater ile de france @# pre-filter to greater ile de france
osmconvert -b=0.374,47.651,4.241,50.261 $< > $@ @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: osm/europe-latest.osm.pbf:
mkdir -p osm @mkdir -p osm
wget http://download.geofabrik.de/europe-latest.osm.pbf -O $@ @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 osm/switzerland-latest.osm: osm/europe-latest.osm.pbf osmconvert
@# pre-filter to greater switzerland @# pre-filter to greater switzerland
osmconvert -b=3.757,44.245,15.579,52.670 $< > $@ @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: gtfs/vitoria/%.txt:
mkdir -p gtfs @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Vitoria-Gasteiz..."
mkdir -p gtfs/vitoria @mkdir -p gtfs
wget https://transitfeeds.com/p/tuvisa-euskotran/239/latest/download -O gtfs/vitoria/gtfs.zip @mkdir -p gtfs/vitoria
cd gtfs/vitoria && unzip -o gtfs.zip @curl --progress-bar https://transitfeeds.com/p/tuvisa-euskotran/239/latest/download > gtfs/vitoria/gtfs.zip
rm gtfs/vitoria/gtfs.zip @cd gtfs/vitoria && unzip -qq -o gtfs.zip
@rm gtfs/vitoria/gtfs.zip
gtfs/stuttgart/%.txt: gtfs/stuttgart/%.txt:
mkdir -p gtfs @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Stuttgart..."
mkdir -p gtfs/stuttgart @mkdir -p gtfs
echo "******************************************************************" @mkdir -p gtfs/stuttgart
echo "* A password is required to access the VVS dataset. Send a mail *" @echo "******************************************************************"
echo "* to brosi@cs.informatik.uni-freiburg.de to receive the password. " @echo "* A password is required to access the VVS dataset. Send a mail *"
echo "******************************************************************" @echo "* to brosi@cs.informatik.uni-freiburg.de to receive the password. "
wget http://www.vvs.de/download/opendata/VVS_GTFS.zip --ask-password --user vvsopendata01 -O gtfs/stuttgart/gtfs.zip @echo "******************************************************************"
cd gtfs/stuttgart && unzip -o gtfs.zip @curl --progress-bar http://www.vvs.de/download/opendata/VVS_GTFS.zip -su vvsopendata01 > gtfs/stuttgart/gtfs.zip
rm gtfs/stuttgart/gtfs.zip @cd gtfs/stuttgart && unzip -qq -o gtfs.zip
@rm gtfs/stuttgart/gtfs.zip
gtfs/paris/%.txt: gtfs/paris/%.txt:
mkdir -p gtfs @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Paris..."
mkdir -p gtfs/paris @mkdir -p gtfs
wget https://transitfeeds.com/p/stif/822/latest/download -O gtfs/paris/gtfs.zip @mkdir -p gtfs/paris
cd gtfs/paris && unzip -o gtfs.zip @curl --progress-bar https://transitfeeds.com/p/stif/822/latest/download > gtfs/paris/gtfs.zip
rm gtfs/paris/gtfs.zip @cd gtfs/paris && unzip -qq -o gtfs.zip
@rm gtfs/paris/gtfs.zip
gtfs/switzerland/%.txt: gtfs/switzerland/%.txt:
mkdir -p gtfs @echo `date +"[%F %T.%3N]"` "EVAL : Downloading GTFS data for Switzerland..."
mkdir -p gtfs/switzerland @mkdir -p gtfs
wget http://gtfs.geops.ch/dl/gtfs_complete.zip -O gtfs/switzerland/gtfs.zip @mkdir -p gtfs/switzerland
cd gtfs/switzerland && unzip -o gtfs.zip @curl --progress-bar http://gtfs.geops.ch/dl/gtfs_complete.zip > gtfs/switzerland/gtfs.zip
rm 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 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 eval.cfg 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/stuttgart/ -c eval.cfg -m all -X $@ @../build/pfaedle -x $< -i gtfs/vitoria/ -c eval.cfg -m all -X $@
osm/paris.osm: osm/paris-latest.osm gtfs/paris/stops.txt eval.cfg 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/paris/ -c eval.cfg -m all -X $@ @../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 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 $@ @../build/pfaedle -x $< -i gtfs/switzerland/ -c eval.cfg -m all -X $@

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,216 @@
# Chair of Algorithms and Datastructures # Chair of Algorithms and Datastructures
# Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de> # Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
[tram, bus, coach, subway, rail, gondola, funicular, ferry]
# Regular expressions and station comparision is
# always case insensitive!
station_normalize_chain:
, -> ' ';
- -> ' ';
— -> ' ';
_ -> ' ';
" -> '';
' -> '';
` -> '';
\( -> ' ';
\) -> ' ';
\[ -> ' ';
\] -> ' ';
/ -> ' ';
'\\' -> ' ';
< -> ' ';
> -> ' ';
& -> '+';
Ä -> Ae;
Ö -> Oe;
Ü -> Ue;
ä -> 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;
Ä -> 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,}$ -> '';
[rail] [rail]
# OSM entities to keep on different levels, as k=v. Applies # OSM entities to keep on different levels, as k=v. Applies
@ -12,8 +222,10 @@
osm_filter_keep: osm_filter_keep:
railway=rail railway=rail
railway=light_rail railway=light_rail
railway=tram
railway=narrow_gauge railway=narrow_gauge
route=rail route=rail
route=light_rail
route=train route=train
public_transport=stop_area|rel_flat public_transport=stop_area|rel_flat
@ -21,10 +233,11 @@ osm_filter_lvl1:
usage=branch usage=branch
osm_filter_lvl2: osm_filter_lvl2:
railway=tram
service=siding
osm_filter_lvl3: osm_filter_lvl3:
service=crossover service=crossover
service=siding
# we cannot completely drop service=yard, because it is often used # we cannot completely drop service=yard, because it is often used
# incorrectly for crossovers # incorrectly for crossovers
service=yard service=yard
@ -213,6 +426,9 @@ line_normalize_chain:
ä -> ae; ä -> ae;
ö -> oe; ö -> oe;
ü -> ue; ü -> ue;
Ä -> Ae;
Ö -> Oe;
Ü -> Ue;
ß -> ss; ß -> ss;
è -> e; è -> e;
é -> e; é -> e;
@ -253,7 +469,21 @@ line_normalize_chain:
# if a character line number is present, delete the numeric part # if a character line number is present, delete the numeric part
^([a-zA-Z]+) [0-9]+$ -> \1; ^([a-zA-Z]+) [0-9]+$ -> \1;
[bus] track_normalize_chain:
'(^| )gleis($| )' -> '';
'(^| )gl\.($| )' -> '';
'(^| )platform($| )' -> '';
'(^| )track($| )' -> '';
'(^| )rail($| )' -> '';
^([a-zA-Z]+) ([0-9]+)$ -> \1\2;
# number/char combs ALWAYS without char
^([0-9]+) ([a-zA-Z]+)$ -> \1;
^([0-9]+)([a-zA-Z]+)$ -> \1;
# delete track numbers greater than 999
^[0-9]{4,}$ -> '';
[bus, coach]
# OSM entities to keep on different levels, as k=v. Applies # OSM entities to keep on different levels, as k=v. Applies
# to nodes, edges and relations. # to nodes, edges and relations.
@ -287,6 +517,18 @@ osm_filter_keep:
psv=yes psv=yes
psv=designated psv=designated
bus:lanes=yes
bus:lanes=designated
bus:lanes=1
lanes:bus=1
lanes:bus=2
lanes:bus=3
lanes:psv=1
lanes:psv=2
lanes:psv=3
trolley_wire=yes trolley_wire=yes
trolleywire=yes trolleywire=yes
trolleybus=yes trolleybus=yes
@ -448,6 +690,14 @@ osm_filter_undirected:
busway:right=opposite_lane busway:right=opposite_lane
psv=opposite_lane psv=opposite_lane
psv=opposite psv=opposite
lanes:psv:backward=1
lanes:psv:backward=2
lanes:bus:backward=1
lanes:bus:backward=2
bus:lanes:backward=yes
bus:lanes:backward=designated
bus:lanes:backward=1
# Nodes that are stations. # Nodes that are stations.
# Only nodes that have been kept during the filtering above will be # Only nodes that have been kept during the filtering above will be
@ -486,11 +736,13 @@ osm_station_group_attrs:
# max distance in meters between a snapped station position and the # max distance in meters between a snapped station position and the
# original station position # original station position
osm_max_snap_distance: 10 , 50, 100 osm_max_snap_distance: 10, 50, 100
osm_max_snap_fallback_distance: 300
osm_max_snap_level: 5 osm_max_snap_level: 5
osm_max_osm_station_distance: 7.5 osm_max_osm_station_distance: 8.0
# sorted by priority, first found attr will be taken # sorted by priority, first found attr will be taken
osm_station_name_attrs: osm_station_name_attrs:
@ -541,7 +793,65 @@ routing_one_way_edge_punish: 5000
# information # information
# routing_line_unmatched_punish_fac: 1.75 # routing_line_unmatched_punish_fac: 1.75
[tram, subway, funicular] [coach]
# 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_lvl0:
highway=motorway
highway=motorway_link
osm_filter_lvl1:
highway=trunk
highway=trunk_link
osm_filter_lvl2:
highway=primary
highway=primary_link
osm_filter_lvl3:
highway=secondary
highway=secondary_link
osm_filter_lvl4:
highway=tertiary
highway=tertiary_link
osm_filter_lvl5:
highway=unclassified
highway=residential
highway=road
highway=service
osm_filter_lvl6:
highway=living_street
highway=pedestrian
psv=no
osm_filter_lvl7:
bus=no
service=siding
access=permissive
access=private
access=no
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_max_snap_level: 5
[tram, subway]
# OSM entities to keep on different levels, as k=v. Applies # OSM entities to keep on different levels, as k=v. Applies
# to nodes, edges and relations. # to nodes, edges and relations.
@ -550,6 +860,7 @@ routing_one_way_edge_punish: 5000
osm_filter_keep: osm_filter_keep:
route=tram route=tram
route=funicular
railway=subway railway=subway
railway=light_rail railway=light_rail
railway=tram railway=tram
@ -562,9 +873,326 @@ osm_filter_keep:
subway=yes subway=yes
tram=yes tram=yes
osm_filter_lv2: osm_filter_lvl2:
service=siding service=siding
osm_filter_lvl3:
railway=funicular
route=funicular
osm_filter_lvl5:
service=crossover
service=yard
# OSM entities to drop, as k=v. Applies to nodes, edges and
# relations.
# Nodes included in non-dropped ways are kept regardless of
# a matching drop filter.
# Ways included in non-dropped relations are kept regardless of
# a matching drop filter.
osm_filter_drop:
area=yes
public_transport=stop_area
type=multipolygon
railway=platform
public_transport=platform
service=alley
# Nodes that should act as "no-hup" nodes. These are nodes
# that are contained in multiple ways, but cannot be used
# to switch from one way to another (for example, a
# track crossing in rail networks)
osm_filter_nohup:
railway:switch=no
railway=railway_crossing
# Edges that should act as one-way nodes.
osm_filter_oneway:
oneway=yes
# Edges that may explicitely be used in
# both directions. May be used to set exception
# to "osm_filter_oneway"
osm_filter_undirected:
# Nodes that are stations.
# Only nodes that have been kept during the filtering above will be
# checked.
osm_filter_station:
public_transport=stop_position
station=subway
station=tram
railway=stop
railway=halt
railway=station
railway=tram_stop
railway=subway_stop
tram_stop=*
stop=*
# Relation fields that should be used for catching the lines that
# occur on an edge. Only relations that have been kept during the
# filtering above will be checked. The 'linename' will be normalized
# according to the rules in line_normalization_chain.
# The 'from_name' and 'to_name' will be normalized according to the
# rules in station_normalization_chain.
# The relations tags are given in the order of their relevance -
# the first normalized tag-value that is not null/empty will be
# taken.
osm_line_relation_tags:
line_name=ref,name # careful, no space after/before comma allowed!
from_name=from
to_name=to
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 50, 100
osm_max_snap_level: 4
# sorted by priority, first found attr will be taken
osm_station_name_attrs:
name
uic_name
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# Punishment (in meters) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 2000
routing_station_distance_punish_fac: 3.14
routing_non_osm_station_punish: 235
# Max angle that should be counted as a full turn
routing_full_turn_angle: 80
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 80
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
[gondola]
# 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:
aerialway=gondola
aerialway=cable_car
aerialway=chair_lift
aerialway=mixed_lift
# 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:
# 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:
aerialway=station
aerialway=stop
public_transport=stop_position
station=subway
station=tram
railway=stop
railway=halt
railway=station
railway=tram_stop
railway=subway_stop
tram_stop=*
stop=*
# Relation fields that should be used for catching the lines that
# occur on an edge. Only relations that have been kept during the
# filtering above will be checked. The 'linename' will be normalized
# according to the rules in line_normalization_chain.
# The 'from_name' and 'to_name' will be normalized according to the
# rules in station_normalization_chain.
# The relations tags are given in the order of their relevance -
# the first normalized tag-value that is not null/empty will be
# taken.
osm_line_relation_tags:
line_name=ref,name # careful, no space after/before comma allowed!
from_name=from
to_name=to
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 50, 100
osm_max_snap_level: 4
# sorted by priority, first found attr will be taken
osm_station_name_attrs:
name
uic_name
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# Punishment (in meters) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 2000
routing_station_distance_punish_fac: 3.14
routing_non_osm_station_punish: 235
# Max angle that should be counted as a full turn
routing_full_turn_angle: 80
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 80
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
[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=funicular
railway=funicular
railway=narrow_gauge
route=tram
railway=subway
railway=light_rail
railway=tram
railway=station
railway=halt
railway=tram_stop
route=subway
route=light_rail
subway=yes
tram=yes
osm_filter_lvl2:
service=siding
osm_filter_lvl3:
route=tram
route=narrow_gauge
railway=subway
railway=narrow_gauge
railway=light_rail
railway=tram
railway=station
railway=halt
railway=tram_stop
route=subway
route=light_rail
subway=yes
tram=yes
osm_filter_lvl5: osm_filter_lvl5:
service=crossover service=crossover
service=yard service=yard
@ -797,206 +1425,3 @@ routing_one_way_meter_punish_fac: 1
# information # information
routing_line_unmatched_punish_fac: 0.5 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,}$ -> '';

@ -1 +1 @@
Subproject commit 5a1f788dde8f334d40505268f71bcc473d1968d8 Subproject commit 63fcb1d54eb4889b376b76cafe140317326b5c56

@ -1 +1 @@
Subproject commit 727ddfecc952e7bd8e4b11ef34436454a50e7532 Subproject commit 32b081e352fc7496a4e2b9a90cf46eecaf7c63fd

View file

@ -25,4 +25,6 @@
#define BOX util::geo::Box<PFAEDLE_PRECISION> #define BOX util::geo::Box<PFAEDLE_PRECISION>
#define POLYLINE util::geo::PolyLine<PFAEDLE_PRECISION> #define POLYLINE util::geo::PolyLine<PFAEDLE_PRECISION>
#define BOX_PADDING 2500
#endif // PFAEDLE_DEF_H_ #endif // PFAEDLE_DEF_H_

View file

@ -2,19 +2,25 @@
// Chair of Algorithms and Data Structures. // Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de> // Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <limits.h>
#include <pwd.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream> #include <fstream>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include "ad/cppgtfs/Parser.h" #include "ad/cppgtfs/Parser.h"
#include "ad/cppgtfs/Writer.h" #include "ad/cppgtfs/Writer.h"
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/config/ConfigReader.h" #include "pfaedle/config/ConfigReader.h"
#include "pfaedle/config/MotConfig.h" #include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/MotConfigReader.h" #include "pfaedle/config/MotConfigReader.h"
#include "pfaedle/eval/Collector.h" #include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/gtfs/Writer.h"
#include "pfaedle/netgraph/Graph.h" #include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/OsmIdSet.h" #include "pfaedle/osm/OsmIdSet.h"
#include "pfaedle/router/ShapeBuilder.h" #include "pfaedle/router/ShapeBuilder.h"
@ -30,13 +36,28 @@ using pfaedle::osm::OsmBuilder;
using pfaedle::config::MotConfig; using pfaedle::config::MotConfig;
using pfaedle::config::Config; using pfaedle::config::Config;
using pfaedle::router::ShapeBuilder; using pfaedle::router::ShapeBuilder;
using configparser::ParseFileExc;
using pfaedle::config::MotConfigReader; using pfaedle::config::MotConfigReader;
using pfaedle::config::ConfigReader; using pfaedle::config::ConfigReader;
using pfaedle::eval::Collector; using pfaedle::eval::Collector;
enum class RetCode {
SUCCESS = 0,
NO_INPUT_FEED = 1,
MULT_FEEDS_NOT_ALWD = 2,
TRIP_NOT_FOUND = 3,
GTFS_PARSE_ERR = 4,
NO_OSM_INPUT = 5,
MOT_CFG_PARSE_ERR = 6,
OSM_PARSE_ERR = 7,
GTFS_WRITE_ERR = 8,
NO_MOT_CFG = 9
};
std::string getMotStr(const MOTs& mots); std::string getMotStr(const MOTs& mots);
std::string getFileNameMotStr(const MOTs& mots); std::string getFileNameMotStr(const MOTs& mots);
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots); MOTs getContMots(const MotConfig& motCfg, const MOTs& mots);
std::vector<std::string> getCfgPaths(const Config& cfg);
// _____________________________________________________________________________ // _____________________________________________________________________________
int main(int argc, char** argv) { int main(int argc, char** argv) {
@ -52,44 +73,90 @@ int main(int argc, char** argv) {
ConfigReader cr; ConfigReader cr;
cr.read(&cfg, argc, argv); cr.read(&cfg, argc, argv);
ad::cppgtfs::gtfs::Feed gtfs; std::vector<pfaedle::gtfs::Feed> gtfs(cfg.feedPaths.size());
// feed containing the shapeas in memory for evaluation
ad::cppgtfs::gtfs::Feed evalFeed;
motCfgReader.parse(cfg.configPaths); std::vector<std::string> cfgPaths = getCfgPaths(cfg);
if (cfg.osmPath.empty()) { try {
motCfgReader.parse(cfgPaths);
} catch (configparser::ParseExc ex) {
LOG(ERROR) << "Could not parse MOT configurations, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::MOT_CFG_PARSE_ERR));
}
if (cfg.osmPath.empty() && !cfg.writeOverpass) {
std::cerr << "No OSM input file specified (-x), see --help." << std::endl; std::cerr << "No OSM input file specified (-x), see --help." << std::endl;
exit(5); exit(static_cast<int>(RetCode::NO_OSM_INPUT));
}
if (motCfgReader.getConfigs().size() == 0) {
LOG(ERROR) << "No MOT configurations specified and no implicit "
"configurations found, see --help.";
exit(static_cast<int>(RetCode::NO_MOT_CFG));
} }
if (cfg.feedPaths.size() == 1) { if (cfg.feedPaths.size() == 1) {
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ..."; if (cfg.inPlace) cfg.outputPath = cfg.feedPaths[0];
ad::cppgtfs::Parser p; if (!cfg.writeOverpass)
p.parse(&gtfs, cfg.feedPaths[0]); LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
LOG(INFO) << "Done."; try {
ad::cppgtfs::Parser p;
p.parse(&gtfs[0], cfg.feedPaths[0]);
if (cfg.evaluate) {
// read the shapes and store them in memory
p.parseShapes(&evalFeed, cfg.feedPaths[0]);
}
} catch (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] << " ...";
ad::cppgtfs::Parser p;
try {
p.parse(&gtfs[i], cfg.feedPaths[i]);
} catch (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.feedPaths.size() > 1) { } else if (cfg.feedPaths.size() > 1) {
std::cerr << "Maximally one input feed allowed." << std::endl; std::cerr << "Multiple feeds only allowed in filter mode." << std::endl;
exit(2); exit(static_cast<int>(RetCode::MULT_FEEDS_NOT_ALWD));
} }
LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size() LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size()
<< " unique MOT configs."; << " unique MOT configs.";
MOTs cmdCfgMots = cfg.mots; MOTs cmdCfgMots = cfg.mots;
ad::cppgtfs::gtfs::Trip* singleTrip = 0; pfaedle::gtfs::Trip* singleTrip = 0;
if (cfg.shapeTripId.size()) { if (cfg.shapeTripId.size()) {
singleTrip = gtfs.getTrips().get(cfg.shapeTripId); if (!cfg.feedPaths.size()) {
std::cout << "No input feed specified, see --help" << std::endl;
exit(static_cast<int>(RetCode::NO_INPUT_FEED));
}
singleTrip = gtfs[0].getTrips().get(cfg.shapeTripId);
if (!singleTrip) { if (!singleTrip) {
LOG(ERROR) << "Trip #" << cfg.shapeTripId << " not found."; LOG(ERROR) << "Trip #" << cfg.shapeTripId << " not found.";
exit(3); exit(static_cast<int>(RetCode::TRIP_NOT_FOUND));
} }
} }
if (cfg.writeOsm.size()) { if (cfg.writeOsm.size()) {
LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ..."; LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ...";
BBoxIdx box(2500); BBoxIdx box(BOX_PADDING);
if (cfg.feedPaths.size()) { for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
box = ShapeBuilder::getPaddedGtfsBox(&gtfs, 2500, cmdCfgMots, ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
cfg.shapeTripId, true); &box);
} }
OsmBuilder osmBuilder; OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts; std::vector<pfaedle::osm::OsmReadOpts> opts;
@ -99,15 +166,33 @@ int main(int argc, char** argv) {
opts.push_back(o.osmBuildOpts); opts.push_back(o.osmBuildOpts);
} }
} }
osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box); try {
exit(0); osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
} catch (xml::XmlFileException ex) {
LOG(ERROR) << "Could not parse OSM data, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::OSM_PARSE_ERR));
}
exit(static_cast<int>(RetCode::SUCCESS));
} else if (cfg.writeOverpass) {
BBoxIdx box(BOX_PADDING);
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
&box);
}
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
for (const auto& o : motCfgReader.getConfigs()) {
if (std::find_first_of(o.mots.begin(), o.mots.end(), cmdCfgMots.begin(),
cmdCfgMots.end()) != o.mots.end()) {
opts.push_back(o.osmBuildOpts);
}
}
osmBuilder.overpassQryWrite(&std::cout, opts, box);
exit(static_cast<int>(RetCode::SUCCESS));
} else if (!cfg.feedPaths.size()) { } else if (!cfg.feedPaths.size()) {
std::cout << "No input feed specified, see --help" << std::endl; std::cout << "No input feed specified, see --help" << std::endl;
exit(1); exit(static_cast<int>(RetCode::NO_INPUT_FEED));
}
if (motCfgReader.getConfigs().size() == 0) {
LOG(WARN) << "No MOT configurations specified, see --help.";
} }
std::vector<double> dfBins; std::vector<double> dfBins;
@ -119,62 +204,76 @@ int main(int argc, char** argv) {
std::string filePost; std::string filePost;
auto usedMots = getContMots(motCfg, cmdCfgMots); auto usedMots = getContMots(motCfg, cmdCfgMots);
if (!usedMots.size()) continue; if (!usedMots.size()) continue;
if (singleTrip && !usedMots.count(singleTrip->getRoute()->getType()))
continue;
if (motCfgReader.getConfigs().size() > 1) if (motCfgReader.getConfigs().size() > 1)
filePost = getFileNameMotStr(usedMots); filePost = getFileNameMotStr(usedMots);
std::string motStr = getMotStr(usedMots); std::string motStr = getMotStr(usedMots);
LOG(INFO) << "Calculating shapes for mots " << motStr; LOG(INFO) << "Calculating shapes for mots " << motStr;
ShapeBuilder shapeBuilder(&gtfs, cmdCfgMots, motCfg, &ecoll, cfg); try {
ShapeBuilder shapeBuilder(&gtfs[0], &evalFeed, cmdCfgMots, motCfg, &ecoll,
cfg);
if (cfg.writeGraph) { if (cfg.writeGraph) {
LOG(INFO) << "Outputting graph.json..."; LOG(INFO) << "Outputting graph.json...";
util::geo::output::GeoGraphJsonOutput out; util::geo::output::GeoGraphJsonOutput out;
std::ofstream fstr(cfg.dbgOutputPath + "/graph.json"); mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
out.print(*shapeBuilder.getGraph(), fstr); std::ofstream fstr(cfg.dbgOutputPath + "/graph.json");
fstr.close(); out.print(*shapeBuilder.getGraph(), fstr);
} fstr.close();
if (singleTrip) {
LOG(INFO) << "Outputting path.json...";
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
util::geo::output::GeoJsonOutput o(pstr);
auto l = shapeBuilder.shapeL(singleTrip);
if (singleTrip->getShape()) {
auto orig = Collector::getWebMercLine(singleTrip->getShape(), -1, -1);
o.print(orig, util::json::Dict{{"ver", "old"}});
} }
o.print(l, util::json::Dict{{"ver", "new"}}); if (singleTrip) {
o.flush(); LOG(INFO) << "Outputting path.json...";
pstr.close(); 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);
exit(0); auto l = shapeBuilder.shapeL(singleTrip);
}
pfaedle::netgraph::Graph ng; o.print(l, util::json::Dict{{"ver", "new"}});
shapeBuilder.shape(&ng); o.flush();
pstr.close();
if (cfg.buildTransitGraph) { exit(static_cast<int>(RetCode::SUCCESS));
util::geo::output::GeoGraphJsonOutput out; }
LOG(INFO) << "Outputting trgraph" + filePost + ".json...";
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph" + filePost + ".json"); pfaedle::netgraph::Graph ng;
out.print(ng, fstr); shapeBuilder.shape(&ng);
fstr.close();
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.print(ng, fstr);
fstr.close();
}
} catch (xml::XmlFileException ex) {
LOG(ERROR) << "Could not parse OSM data, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::OSM_PARSE_ERR));
} }
} }
if (cfg.evaluate) ecoll.printStats(&std::cout); if (cfg.evaluate) ecoll.printStats(&std::cout);
if (cfg.feedPaths.size()) { if (cfg.feedPaths.size()) {
LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ..."; try {
ad::cppgtfs::Writer w; mkdir(cfg.outputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
w.write(&gtfs, cfg.outputPath); LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ...";
pfaedle::gtfs::Writer w;
w.write(&gtfs[0], cfg.outputPath);
} catch (ad::cppgtfs::WriterException ex) {
LOG(ERROR) << "Could not write final GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_WRITE_ERR));
}
} }
return (0); return static_cast<int>(RetCode::SUCCESS);
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
@ -183,7 +282,7 @@ std::string getMotStr(const MOTs& mots) {
std::string motStr; std::string motStr;
for (const auto& mot : mots) { for (const auto& mot : mots) {
if (first) motStr += ", "; if (first) motStr += ", ";
motStr += "<" + Route::getTypeString(mot) + ">"; motStr += "<" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot) + ">";
first = true; first = true;
} }
@ -194,7 +293,7 @@ std::string getMotStr(const MOTs& mots) {
std::string getFileNameMotStr(const MOTs& mots) { std::string getFileNameMotStr(const MOTs& mots) {
std::string motStr; std::string motStr;
for (const auto& mot : mots) { for (const auto& mot : mots) {
motStr += "-" + Route::getTypeString(mot); motStr += "-" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot);
} }
return motStr; return motStr;
@ -211,3 +310,73 @@ MOTs getContMots(const MotConfig& motCfg, const MOTs& mots) {
return ret; return ret;
} }
// _____________________________________________________________________________
std::vector<std::string> getCfgPaths(const Config& cfg) {
if (cfg.configPaths.size()) return cfg.configPaths;
std::vector<std::string> ret;
// parse implicit paths
const char* homedir = 0;
char* buf = 0;
if ((homedir = getenv("HOME")) == 0) {
homedir = "";
struct passwd pwd;
struct passwd* result;
size_t bufsize;
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == static_cast<size_t>(-1)) bufsize = 0x4000;
buf = static_cast<char*>(malloc(bufsize));
if (buf != 0) {
getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
if (result != NULL) homedir = result->pw_dir;
}
}
// install prefix global configuration path, if available
{
auto path = std::string(INSTALL_PREFIX) +
std::string(XDG_CONFIG_DIRS_DEFAULT) + "/" + "pfaedle" + "/" +
CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
if (is.good()) {
ret.push_back(path);
LOG(DEBUG) << "Found implicit config file " << path;
}
}
// local user configuration path, if available
{
auto path = std::string(homedir) + XDG_CONFIG_HOME_SUFFIX + "/" +
"pfaedle" + "/" + CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
if (is.good()) {
ret.push_back(path);
LOG(DEBUG) << "Found implicit config file " << path;
}
}
if (buf) free(buf);
// CWD
{
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd))) {
auto path = std::string(cwd) + "/" + CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
if (is.good()) {
ret.push_back(path);
LOG(DEBUG) << "Found implicit config file " << path;
}
}
}
return ret;
}

View file

@ -7,4 +7,7 @@
// version number from cmake version module // version number from cmake version module
#define VERSION_FULL "@VERSION_GIT_FULL@" #define VERSION_FULL "@VERSION_GIT_FULL@"
// version number from cmake version module
#define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
#endif // SRC_PFAEDLE_CONFIG_H_N #endif // SRC_PFAEDLE_CONFIG_H_N

View file

@ -26,67 +26,96 @@ static const char* AUTHORS = "Patrick Brosi <brosi@informatik.uni-freiburg.de>";
// _____________________________________________________________________________ // _____________________________________________________________________________
void ConfigReader::help(const char* bin) { void ConfigReader::help(const char* bin) {
std::cout std::cout << std::setfill(' ') << std::left << "pfaedle GTFS map matcher "
<< std::setfill(' ') << std::left << "pfaedle GTFS map matcher\n" << VERSION_FULL << "\n(built " << __DATE__ << " " << __TIME__
<< VERSION_FULL << " (built " << __DATE__ << " " << __TIME__ << " with geometry precision <" << PFAEDLE_PRECISION_STR << ">)\n\n"
<< " with geometry precision <" << PFAEDLE_PRECISION_STR << ">)\n\n" << "(C) " << YEAR << " " << COPY << "\n"
<< "(C) " << YEAR << " " << COPY << "\n" << "Authors: " << AUTHORS << "\n\n"
<< "Authors: " << AUTHORS << "\n\n" << "Usage: " << bin
<< "Usage: " << bin << " -x <OSM FILE> -c <CFG FILE> <GTFS FEED>\n\n" << " -x <OSM FILE> <GTFS FEED>\n\n"
<< "Allowed options:\n\n" << "Allowed options:\n\n"
<< "General:\n" << "General:\n"
<< std::setw(35) << " -v [ --version ]" << std::setw(35) << " -v [ --version ]"
<< "print version\n" << "print version\n"
<< std::setw(35) << " -h [ --help ]" << std::setw(35) << " -h [ --help ]"
<< "show this help message\n" << "show this help message\n"
<< std::setw(35) << " -D [ --drop-shapes ]" << std::setw(35) << " -D [ --drop-shapes ]"
<< "drop shapes already present in the feed and recalculate them\n" << "drop shapes already present in the feed and\n"
<< "\nInput:\n" << std::setw(35) << " "
<< std::setw(35) << " -c [ --config ] arg" << " recalculate them\n"
<< "pfaedle config file\n" << "\nInput:\n"
<< std::setw(35) << " -i [ --input ] arg" << std::setw(35) << " -c [ --config ] arg"
<< "gtfs feed(s), may also be given as positional parameter (see usage)\n" << "pfaedle config file\n"
<< std::setw(35) << " -x [ --osm-file ] arg" << std::setw(35) << " -i [ --input ] arg"
<< "OSM xml input file\n" << "gtfs feed(s), may also be given as positional\n"
<< std::setw(35) << " -m [ --mots ] arg (=all)" << std::setw(35) << " "
<< "MOTs to calculate shapes for, comma separated, either as string " << " parameter (see usage)\n"
"{all,\n" << std::setw(35) << " -x [ --osm-file ] arg"
<< std::setw(35) << " " << "OSM xml input file\n"
<< "tram | streetcar, subway | metro, rail | train, bus, ferry | boat | " << std::setw(35) << " -m [ --mots ] arg (=all)"
"\n" << "MOTs to calculate shapes for, comma sep.,\n"
<< std::setw(35) << " " << std::setw(35) << " "
<< "ship, cableclar, gondola, funicular} or as GTFS mot codes\n" << " either as string "
<< "\nOutput:\n" "{all, tram | streetcar,\n"
<< std::setw(35) << " -o [ --output ] arg (=gtfs-out)" << std::setw(35) << " "
<< "GTFS output path\n" << " subway | metro, rail | train, bus,\n"
<< std::setw(35) << " -X [ --osm-out ] arg" << std::setw(35) << " "
<< "if specified, a filtered OSM file will be written to <arg>\n" << " ferry | boat | ship, cablecar, gondola,\n"
<< "\nDebug Output:\n" << std::setw(35) << " "
<< std::setw(35) << " -d [ --dbg-path ] arg (=geo)" << " funicular, coach} or as GTFS mot codes\n"
<< "output path for debug files\n" << "\nOutput:\n"
<< std::setw(35) << " --write-trgraph" << std::setw(35) << " -o [ --output ] arg (=gtfs-out)"
<< "write transit graph as GeoJSON to <dbg-path>/trgraph.json\n" << "GTFS output path\n"
<< std::setw(35) << " --write-graph" << std::setw(35) << " -X [ --osm-out ] arg"
<< "write routing graph as GeoJSON to <dbg-path>/graph.json\n" << "if specified, a filtered OSM file will be\n"
<< std::setw(35) << " --write-cgraph" << std::setw(35) << " "
<< "if -T is set, write combination graph as GeoJSON to " << " written to <arg>\n"
"<dbg-path>/combgraph.json\n" << std::setw(35) << " --inplace"
<< std::setw(35) << " --method arg (=global)" << "overwrite input GTFS feed with output feed\n"
<< "matching method to use, either 'global' (based on HMM), 'greedy' or " << "\nDebug Output:\n"
"'greedy2'\n" << std::setw(35) << " -d [ --dbg-path ] arg (=geo)"
<< std::setw(35) << " --eval" << "output path for debug files\n"
<< "evaluate existing shapes against matched shapes and print results\n" << std::setw(35) << " --write-trgraph"
<< std::setw(35) << " --eval-path arg (=.)" << "write transit graph as GeoJSON to\n"
<< "path for eval file output\n" << std::setw(35) << " "
<< std::setw(35) << " --eval-df-bins arg (= )" << " <dbg-path>/trgraph.json\n"
<< "bins to use for d_f histogram, comma separated (e.g. 10,20,30,40)\n" << std::setw(35) << " --write-graph"
<< "\nMisc:\n" << "write routing graph as GeoJSON to\n"
<< std::setw(35) << " -T [ --trip-id ] arg" << std::setw(35) << " "
<< "Do routing only for trip <arg>, write result to\n" << " <dbg-path>/graph.json\n"
<< std::setw(35) << " " << std::setw(35) << " --write-cgraph"
<< "<dbg-path>/path.json\n" << "if -T is set, write combination graph as\n"
<< std::setw(35) << " --grid-size arg (=2000)" << std::setw(35) << " "
<< "Grid cell size\n"; << " 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"
<< std::setw(35) << " "
<< " to <dbg-path>/path.json\n"
<< 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";
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
@ -101,6 +130,7 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
{"drop-shapes", required_argument, 0, 'D'}, {"drop-shapes", required_argument, 0, 'D'},
{"mots", required_argument, NULL, 'm'}, {"mots", required_argument, NULL, 'm'},
{"grid-size", required_argument, 0, 'g'}, {"grid-size", required_argument, 0, 'g'},
{"overpass", no_argument, 0, 'a'},
{"osm-out", required_argument, 0, 'X'}, {"osm-out", required_argument, 0, 'X'},
{"trip-id", required_argument, 0, 'T'}, {"trip-id", required_argument, 0, 'T'},
{"write-graph", no_argument, 0, 1}, {"write-graph", no_argument, 0, 1},
@ -113,6 +143,8 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
{"dbg-path", required_argument, 0, 'd'}, {"dbg-path", required_argument, 0, 'd'},
{"version", no_argument, 0, 'v'}, {"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"inplace", no_argument, 0, 9},
{"use-route-cache", no_argument, 0, 8},
{0, 0, 0, 0}}; {0, 0, 0, 0}};
char c; char c;
@ -140,6 +172,9 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
case 7: case 7:
cfg->evalDfBins = optarg; cfg->evalDfBins = optarg;
break; break;
case 8:
cfg->useCaching = true;
break;
case 'o': case 'o':
cfg->outputPath = optarg; cfg->outputPath = optarg;
break; break;
@ -170,6 +205,12 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
case 'd': case 'd':
cfg->dbgOutputPath = optarg; cfg->dbgOutputPath = optarg;
break; break;
case 'a':
cfg->writeOverpass = true;
break;
case 9:
cfg->inPlace = true;
break;
case 'v': case 'v':
std::cout << "pfaedle " << VERSION_FULL << " (built " << __DATE__ << " " std::cout << "pfaedle " << VERSION_FULL << " (built " << __DATE__ << " "
<< __TIME__ << " with geometry precision <" << __TIME__ << " with geometry precision <"
@ -204,7 +245,8 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
auto v = util::split(motStr, ','); auto v = util::split(motStr, ',');
for (const auto& motStr : v) { for (const auto& motStr : v) {
const auto& mots = Route::getTypesFromString(util::trim(motStr)); const auto& mots =
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(util::trim(motStr));
cfg->mots.insert(mots.begin(), mots.end()); cfg->mots.insert(mots.begin(), mots.end());
} }

View file

@ -7,6 +7,7 @@
#include "pfaedle/config/MotConfigReader.h" #include "pfaedle/config/MotConfigReader.h"
#include "util/Misc.h" #include "util/Misc.h"
#include "util/String.h" #include "util/String.h"
#include "util/log/Log.h"
using pfaedle::config::MotConfigReader; using pfaedle::config::MotConfigReader;
using pfaedle::config::MotConfig; using pfaedle::config::MotConfig;
@ -23,341 +24,336 @@ MotConfigReader::MotConfigReader() {}
// _____________________________________________________________________________ // _____________________________________________________________________________
void MotConfigReader::parse(const std::vector<std::string>& paths) { void MotConfigReader::parse(const std::vector<std::string>& paths) {
ConfigFileParser p;
// parse explicitely given paths
for (const auto& s : paths) { for (const auto& s : paths) {
ConfigFileParser p; LOG(DEBUG) << "Reading config file " << s;
p.parse(s); p.parse(s);
}
for (const auto& sec : p.getSecs()) { for (const auto& sec : p.getSecs()) {
MotConfig curCfg; MotConfig curCfg;
std::string secStr = sec.first; std::string secStr = sec.first;
if (p.hasKey(secStr, "osm_filter_keep")) { if (p.hasKey(secStr, "osm_filter_keep")) {
for (const auto& kvs : p.getStrArr(sec.first, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
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)) {
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
auto fRule = getFRule(kvs); auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert( curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
} }
} }
}
for (uint8_t i = 0; i < 7; i++) { if (p.hasKey(secStr, "osm_filter_drop")) {
std::string name = for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) {
std::string("osm_filter_lvl") + std::to_string(i + 1); auto fRule = getFRule(kvs);
if (p.hasKey(secStr, name)) { curCfg.osmBuildOpts.dropFilter[fRule.kv.first].insert(
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) { osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
} }
}
if (p.hasKey(secStr, "osm_filter_drop")) { if (p.hasKey(secStr, "osm_max_snap_level")) {
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) { curCfg.osmBuildOpts.maxSnapLevel =
auto fRule = getFRule(kvs); p.getInt(sec.first, "osm_max_snap_level");
curCfg.osmBuildOpts.dropFilter[fRule.kv.first].insert( } else {
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); curCfg.osmBuildOpts.maxSnapLevel = 7;
} }
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
} }
}
if (p.hasKey(secStr, "osm_max_snap_level")) { if (p.hasKey(secStr, "osm_filter_oneway")) {
curCfg.osmBuildOpts.maxSnapLevel = for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_oneway", ' ')) {
p.getInt(sec.first, "osm_max_snap_level"); auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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));
}
}
if (p.hasKey(secStr, "osm_track_number_tags")) {
for (const std::string& r :
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
curCfg.osmBuildOpts.statAttrRules.platformRule.push_back(
getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "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));
}
}
if (p.hasKey(secStr, "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});
}
}
if (p.hasKey(secStr, "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;
else if (rule.first == "to_name")
curCfg.osmBuildOpts.relLinerules.toNameRule = tags;
else if (rule.first == "line_name")
curCfg.osmBuildOpts.relLinerules.sNameRule = tags;
}
}
if (p.hasKey(secStr, "osm_max_snap_distance")) {
curCfg.osmBuildOpts.maxSnapDistances =
p.getDoubleArr(secStr, "osm_max_snap_distance", ',');
} else {
curCfg.osmBuildOpts.maxSnapDistances.push_back(50);
}
if (p.hasKey(secStr, "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;
}
if (p.hasKey(secStr, "osm_max_osm_station_distance")) {
curCfg.osmBuildOpts.maxOsmStationDistance =
p.getDouble(secStr, "osm_max_osm_station_distance");
} else {
curCfg.osmBuildOpts.maxOsmStationDistance = 5;
}
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
curCfg.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()) /
8;
}
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)) {
double v = p.getDouble(sec.first, name);
curCfg.routingOpts.levelPunish[i] = v;
} else { } else {
curCfg.osmBuildOpts.maxSnapLevel = 7; curCfg.routingOpts.levelPunish[i] = 1;
} }
}
if (p.hasKey(secStr, "osm_filter_nohup")) { if (p.hasKey(secStr, "routing_full_turn_punish")) {
for (const auto& kvs : curCfg.routingOpts.fullTurnPunishFac =
p.getStrArr(sec.first, "osm_filter_nohup", ' ')) { p.getDouble(secStr, "routing_full_turn_punish");
auto fRule = getFRule(kvs); }
curCfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert( if (p.hasKey(secStr, "routing_full_turn_angle")) {
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags))); 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")) {
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")) {
curCfg.routingOpts.passThruStationsPunish =
p.getDouble(secStr, "routing_pass_thru_station_punish");
}
if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) {
curCfg.routingOpts.oneWayPunishFac =
p.getDouble(secStr, "routing_one_way_meter_punish_fac");
}
if (p.hasKey(secStr, "routing_one_way_edge_punish")) {
curCfg.routingOpts.oneWayEdgePunish =
p.getDouble(secStr, "routing_one_way_edge_punish");
}
if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) {
curCfg.routingOpts.lineUnmatchedPunishFact =
p.getDouble(secStr, "routing_line_unmatched_punish_fac");
}
if (p.hasKey(secStr, "routing_platform_unmatched_punish")) {
curCfg.routingOpts.platformUnmatchedPen =
p.getDouble(secStr, "routing_platform_unmatched_punish");
}
if (p.hasKey(secStr, "routing_non_osm_station_punish")) {
curCfg.routingOpts.nonOsmPen =
p.getDouble(secStr, "routing_non_osm_station_punish");
} else {
curCfg.routingOpts.nonOsmPen = 0;
}
if (p.hasKey(secStr, "routing_station_distance_punish_fac")) {
curCfg.routingOpts.stationDistPenFactor =
p.getDouble(secStr, "routing_station_distance_punish_fac");
} else {
curCfg.routingOpts.stationDistPenFactor = 1;
}
if (p.hasKey(secStr, "station_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
curCfg.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,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">",
p.getVal(secStr, "station_normalize_chain").file);
}
}
if (p.hasKey(secStr, "track_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
curCfg.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,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">",
p.getVal(secStr, "track_normalize_chain").file);
}
}
if (p.hasKey(secStr, "line_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
curCfg.osmBuildOpts.lineNormzer =
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,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">",
p.getVal(secStr, "station_normalize_chain").file);
}
}
bool found = false;
for (auto& cfg : _cfgs) {
if (cfg == curCfg) {
for (auto mot :
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr)) {
cfg.mots.insert(mot);
} }
found = true;
break;
} }
}
if (p.hasKey(secStr, "osm_filter_oneway")) { if (!found) {
for (const auto& kvs : curCfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr);
p.getStrArr(sec.first, "osm_filter_oneway", ' ')) { _cfgs.push_back(curCfg);
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "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));
}
}
if (p.hasKey(secStr, "osm_track_number_tags")) {
for (const std::string& r :
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
curCfg.osmBuildOpts.statAttrRules.platformRule.push_back(
getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "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));
}
}
if (p.hasKey(secStr, "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});
}
}
if (p.hasKey(secStr, "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;
else if (rule.first == "to_name")
curCfg.osmBuildOpts.relLinerules.toNameRule = tags;
else if (rule.first == "line_name")
curCfg.osmBuildOpts.relLinerules.sNameRule = tags;
}
}
if (p.hasKey(secStr, "osm_max_snap_distance")) {
curCfg.osmBuildOpts.maxSnapDistances =
p.getDoubleArr(secStr, "osm_max_snap_distance", ',');
} else {
curCfg.osmBuildOpts.maxSnapDistances.push_back(50);
}
if (p.hasKey(secStr, "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;
}
if (p.hasKey(secStr, "osm_max_group_search_distance")) {
curCfg.osmBuildOpts.maxGroupSearchDistance =
p.getDouble(secStr, "osm_max_group_search_distance");
} else {
curCfg.osmBuildOpts.maxGroupSearchDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.end()) *
4;
}
if (p.hasKey(secStr, "osm_max_osm_station_distance")) {
curCfg.osmBuildOpts.maxOsmStationDistance =
p.getDouble(secStr, "osm_max_osm_station_distance");
} else {
curCfg.osmBuildOpts.maxOsmStationDistance = 5;
}
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
curCfg.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()) /
8;
}
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)) {
double v = p.getDouble(sec.first, name);
curCfg.routingOpts.levelPunish[i] = v;
} else {
curCfg.routingOpts.levelPunish[i] = 1;
}
}
if (p.hasKey(secStr, "routing_full_turn_punish")) {
curCfg.routingOpts.fullTurnPunishFac =
p.getDouble(secStr, "routing_full_turn_punish");
}
if (p.hasKey(secStr, "routing_full_turn_angle")) {
double ang = p.getDouble(secStr, "routing_full_turn_angle");
curCfg.routingOpts.fullTurnAngle = ang;
} else {
curCfg.routingOpts.fullTurnAngle = 5;
}
if (p.hasKey(secStr, "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")) {
curCfg.routingOpts.passThruStationsPunish =
p.getDouble(secStr, "routing_pass_thru_station_punish");
}
if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) {
curCfg.routingOpts.oneWayPunishFac =
p.getDouble(secStr, "routing_one_way_meter_punish_fac");
}
if (p.hasKey(secStr, "routing_one_way_edge_punish")) {
curCfg.routingOpts.oneWayEdgePunish =
p.getDouble(secStr, "routing_one_way_edge_punish");
}
if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) {
curCfg.routingOpts.lineUnmatchedPunishFact =
p.getDouble(secStr, "routing_line_unmatched_punish_fac");
}
if (p.hasKey(secStr, "routing_platform_unmatched_punish")) {
curCfg.routingOpts.platformUnmatchedPen =
p.getDouble(secStr, "routing_platform_unmatched_punish");
}
if (p.hasKey(secStr, "routing_non_osm_station_punish")) {
curCfg.routingOpts.nonOsmPen =
p.getDouble(secStr, "routing_non_osm_station_punish");
} else {
curCfg.routingOpts.nonOsmPen = 0;
}
if (p.hasKey(secStr, "routing_station_distance_punish_fac")) {
curCfg.routingOpts.stationDistPenFactor =
p.getDouble(secStr, "routing_station_distance_punish_fac");
} else {
curCfg.routingOpts.stationDistPenFactor = 1;
}
if (p.hasKey(secStr, "station_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
curCfg.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,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">", s);
}
}
if (p.hasKey(secStr, "track_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
curCfg.osmBuildOpts.trackNormzer =
trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "track_normalize_chain").line,
p.getVal(secStr, "station_normalize_chain").pos,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">", s);
}
}
if (p.hasKey(secStr, "line_normalize_chain")) {
try {
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
curCfg.osmBuildOpts.lineNormzer =
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,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">", s);
}
}
bool found = false;
for (auto& cfg : _cfgs) {
if (cfg == curCfg) {
for (auto mot : Route::getTypesFromString(secStr)) {
cfg.mots.insert(mot);
}
found = true;
break;
}
}
if (!found) {
curCfg.mots = Route::getTypesFromString(secStr);
_cfgs.push_back(curCfg);
}
} }
} }
} }

View file

@ -5,6 +5,30 @@
#ifndef PFAEDLE_CONFIG_MOTCONFIGREADER_H_ #ifndef PFAEDLE_CONFIG_MOTCONFIGREADER_H_
#define PFAEDLE_CONFIG_MOTCONFIGREADER_H_ #define PFAEDLE_CONFIG_MOTCONFIGREADER_H_
#include "pfaedle/_config.h"
#ifndef HOME_VAR
#define HOME_VAR "HOME"
#endif
#ifndef XDG_DATA_HOME_SUFFIX
#define XDG_DATA_HOME_SUFFIX "/.local/share"
#endif
#ifndef XDG_CONFIG_HOME_SUFFIX
#define XDG_CONFIG_HOME_SUFFIX "/.config"
#endif
#ifndef XDG_CACHE_HOME_SUFFIX
#define XDG_CACHE_HOME_SUFFIX "/.cache"
#endif
#ifndef XDG_DATA_DIRS_DEFAULT
#define XDG_DATA_DIRS_DEFAULT "/usr/local/share"
#endif
#ifndef XDG_CONFIG_DIRS_DEFAULT
#define XDG_CONFIG_DIRS_DEFAULT "/etc"
#endif
#ifndef CFG_FILE_NAME
#define CFG_FILE_NAME "pfaedle.cfg"
#endif
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <set> #include <set>

View file

@ -28,6 +28,9 @@ struct Config {
writeCombGraph(false), writeCombGraph(false),
evaluate(false), evaluate(false),
buildTransitGraph(false), buildTransitGraph(false),
useCaching(false),
writeOverpass(false),
inPlace(false),
gridSize(2000) {} gridSize(2000) {}
std::string dbgOutputPath; std::string dbgOutputPath;
std::string solveMethod; std::string solveMethod;
@ -46,6 +49,9 @@ struct Config {
bool writeCombGraph; bool writeCombGraph;
bool evaluate; bool evaluate;
bool buildTransitGraph; bool buildTransitGraph;
bool useCaching;
bool writeOverpass;
bool inPlace;
double gridSize; double gridSize;
std::string toString() { std::string toString() {
@ -60,6 +66,8 @@ struct Config {
<< "write-graph: " << writeGraph << "\n" << "write-graph: " << writeGraph << "\n"
<< "write-cgraph: " << writeCombGraph << "\n" << "write-cgraph: " << writeCombGraph << "\n"
<< "grid-size: " << gridSize << "\n" << "grid-size: " << gridSize << "\n"
<< "use-cache: " << useCaching << "\n"
<< "write-overpass: " << writeOverpass << "\n"
<< "feed-paths: "; << "feed-paths: ";
for (const auto& p : feedPaths) { for (const auto& p : feedPaths) {

View file

@ -18,14 +18,14 @@
using util::geo::PolyLine; using util::geo::PolyLine;
using ad::cppgtfs::gtfs::Trip; using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape; using ad::cppgtfs::gtfs::Shape;
using pfaedle::eval::Collector; using pfaedle::eval::Collector;
using pfaedle::eval::Result; using pfaedle::eval::Result;
using util::geo::output::GeoJsonOutput; using util::geo::output::GeoJsonOutput;
// _____________________________________________________________________________ // _____________________________________________________________________________
double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS, double Collector::add(const Trip* t, const Shape* oldS, const Shape& newS,
const std::vector<double>& newTripDists) { const std::vector<double>& newTripDists) {
if (!oldS) { if (!oldS) {
_noOrigShp++; _noOrigShp++;
@ -51,7 +51,7 @@ double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS,
(--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists); (--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists);
std::vector<double> newDists; std::vector<double> newDists;
LINE newL = getWebMercLine(newS, -1, -1, &newDists); LINE newL = getWebMercLine(&newS, -1, -1, &newDists);
std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json"); std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json");
GeoJsonOutput gjout(fstr); GeoJsonOutput gjout(fstr);
@ -123,19 +123,19 @@ double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS,
6378137.0)) - 6378137.0)) -
1.5707965); 1.5707965);
if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS)) { if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS.getId())) {
fd = _dCache[oldS][newS]; fd = _dCache[oldS][newS.getId()];
} else { } else {
fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac; fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac;
_dCache[oldS][newS] = fd; _dCache[oldS][newS.getId()] = fd;
} }
if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS)) { if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS.getId())) {
unmatchedSegments = _dACache[oldS][newS].first; unmatchedSegments = _dACache[oldS][newS.getId()].first;
unmatchedSegmentsLength = _dACache[oldS][newS].second; unmatchedSegmentsLength = _dACache[oldS][newS.getId()].second;
} else { } else {
auto dA = getDa(oldSegs, newSegs); auto dA = getDa(oldSegs, newSegs);
_dACache[oldS][newS] = dA; _dACache[oldS][newS.getId()] = dA;
unmatchedSegments = dA.first; unmatchedSegments = dA.first;
unmatchedSegmentsLength = dA.second; unmatchedSegmentsLength = dA.second;
} }
@ -199,6 +199,8 @@ std::vector<LINE> Collector::segmentize(
// get first half of geometry, and search for start point there! // get first half of geometry, and search for start point there!
size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) - size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) -
dists.begin(); 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)); POLYLINE l(LINE(shape.begin(), shape.begin() + before + 1));
auto lastLp = l.projectOn(cuts.front().first); auto lastLp = l.projectOn(cuts.front().first);

View file

@ -12,11 +12,12 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "ad/cppgtfs/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/Def.h" #include "pfaedle/Def.h"
#include "pfaedle/eval/Result.h" #include "pfaedle/eval/Result.h"
#include "util/geo/Geo.h" #include "util/geo/Geo.h"
using ad::cppgtfs::gtfs::Trip; using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape; using ad::cppgtfs::gtfs::Shape;
namespace pfaedle { namespace pfaedle {
@ -37,7 +38,7 @@ class Collector {
// Add a shape found by our tool newS for a trip t with newly calculated // Add a shape found by our tool newS for a trip t with newly calculated
// station dist values with the old shape oldS // station dist values with the old shape oldS
double add(const Trip* t, const Shape* oldS, const Shape* newS, double add(const Trip* t, const Shape* oldS, const Shape& newS,
const std::vector<double>& newDists); const std::vector<double>& newDists);
// Return the set of all Result objects // Return the set of all Result objects
@ -65,8 +66,8 @@ class Collector {
std::set<Result> _results; std::set<Result> _results;
std::set<Result> _resultsAN; std::set<Result> _resultsAN;
std::set<Result> _resultsAL; std::set<Result> _resultsAL;
std::map<const Shape*, std::map<const Shape*, double> > _dCache; std::map<const Shape*, std::map<std::string, double> > _dCache;
std::map<const Shape*, std::map<const Shape*, std::pair<size_t, double> > > std::map<const Shape*, std::map<std::string, std::pair<size_t, double> > >
_dACache; _dACache;
size_t _noOrigShp; size_t _noOrigShp;

View file

@ -5,9 +5,10 @@
#ifndef PFAEDLE_EVAL_RESULT_H_ #ifndef PFAEDLE_EVAL_RESULT_H_
#define PFAEDLE_EVAL_RESULT_H_ #define PFAEDLE_EVAL_RESULT_H_
#include "pfaedle/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Feed.h"
using ad::cppgtfs::gtfs::Trip; using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape; using ad::cppgtfs::gtfs::Shape;
namespace pfaedle { namespace pfaedle {

37
src/pfaedle/gtfs/Feed.h Normal file
View file

@ -0,0 +1,37 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_FEED_H_
#define PFAEDLE_GTFS_FEED_H_
#include <string>
#include "Route.h"
#include "Service.h"
#include "ShapeContainer.h"
#include "StopTime.h"
#include "ad/cppgtfs/gtfs/ContContainer.h"
#include "ad/cppgtfs/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/NullContainer.h"
#include "ad/cppgtfs/gtfs/Stop.h"
#include "ad/cppgtfs/gtfs/StopTime.h"
#include "ad/cppgtfs/gtfs/Trip.h"
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>
Feed;
typedef ad::cppgtfs::gtfs::TripB<StopTime<ad::cppgtfs::gtfs::Stop>, Service,
Route, Shape>
Trip;
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_FEED_H_

61
src/pfaedle/gtfs/Route.h Normal file
View file

@ -0,0 +1,61 @@
// 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_

View file

@ -0,0 +1,43 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_SERVICE_H_
#define PFAEDLE_GTFS_SERVICE_H_
#include <string>
#include "ad/cppgtfs/gtfs/Service.h"
#include "util/Misc.h"
namespace pfaedle {
namespace gtfs {
class Service {
public:
typedef std::string Ref;
static std::string getId(Ref r) { return r; }
explicit Service(const string& id) : _id(id) {}
Service(const string& id, uint8_t serviceDays,
ad::cppgtfs::gtfs::ServiceDate start,
ad::cppgtfs::gtfs::ServiceDate end)
: _id(id) {
UNUSED(serviceDays);
UNUSED(start);
UNUSED(end);
}
const std::string& getId() const { return _id; }
void addException(const ad::cppgtfs::gtfs::ServiceDate& d,
ad::cppgtfs::gtfs::Service::EXCEPTION_TYPE t) {
UNUSED(d);
UNUSED(t);
}
private:
std::string _id;
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_SERVICE_H_

View file

@ -0,0 +1,69 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_SHAPECONTAINER_H_
#define PFAEDLE_GTFS_SHAPECONTAINER_H_
#include <fstream>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include "ad/cppgtfs/gtfs/Shape.h"
#include "ad/cppgtfs/gtfs/flat/Shape.h"
#include "util/Misc.h"
namespace pfaedle {
namespace gtfs {
struct Shape {
explicit Shape(const std::string& id) : id(id) {}
typedef std::string Ref;
static std::string getId(Ref r) { return r; }
template <typename T>
bool addPoint(T p) {
UNUSED(p);
return true;
}
const std::string& getId() const { return id; }
std::string id;
};
template <typename T>
class ShapeContainer {
public:
ShapeContainer();
~ShapeContainer();
T* add(const T& obj);
bool remove(const std::string& id);
const T* get(const std::string& id) const;
T* get(const std::string& id);
const std::string getRef(const std::string& id) const;
std::string getRef(const std::string& id);
size_t size() const;
void finalize() {}
bool has(const std::string& id) const;
std::string add(const ad::cppgtfs::gtfs::Shape& s);
void open();
bool nextStoragePt(ad::cppgtfs::gtfs::flat::ShapePoint* ret);
private:
std::set<std::string> _ids;
std::fstream _storage;
size_t _ptr;
size_t _max;
std::string _curId;
std::stringstream _writeBuffer;
};
#include "ShapeContainer.tpp"
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_SHAPECONTAINER_H_

View file

@ -0,0 +1,154 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <unistd.h>
#include <string>
// ____________________________________________________________________________
template <typename T>
ShapeContainer<T>::ShapeContainer() {
std::string f = ".pfaedle-tmp";
while (access(f.c_str(), F_OK) != -1) {
std::stringstream ss;
ss << ".pfaedle-tmp-";
ss << std::rand();
f = ss.str().c_str();
}
_storage.open(f, std::fstream::in | std::fstream::out | std::fstream::trunc);
// immediately unlink
unlink(f.c_str());
if (!_storage.good()) {
std::cerr << "Could not open temporary file " << f << std::endl;
exit(1);
}
}
// ____________________________________________________________________________
template <typename T>
ShapeContainer<T>::~ShapeContainer() {
_storage.close();
}
// ____________________________________________________________________________
template <typename T>
T* ShapeContainer<T>::add(const T& ent) {
_ids.insert(ent.getId());
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
template <typename T>
bool ShapeContainer<T>::remove(const std::string& id) {
_ids.erase(id);
return true;
}
// ____________________________________________________________________________
template <typename T>
T* ShapeContainer<T>::get(const std::string& id) {
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
template <typename T>
const T* ShapeContainer<T>::get(const std::string& id) const {
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
template <typename T>
bool ShapeContainer<T>::has(const std::string& id) const {
return _ids.count(id);
}
// ____________________________________________________________________________
template <typename T>
size_t ShapeContainer<T>::size() const {
return _ids.size();
}
// ____________________________________________________________________________
template <typename T>
std::string ShapeContainer<T>::add(const ad::cppgtfs::gtfs::Shape& s) {
if (has(s.getId())) return s.getId();
_ids.insert(s.getId());
_writeBuffer << s.getId() << '\t' << s.getPoints().size();
_writeBuffer << std::setprecision(11);
for (auto p : s.getPoints()) {
_writeBuffer << " " << p.lat << " " << p.lng << " " << p.travelDist;
}
// entries are newline separated
_writeBuffer << '\n';
if (_writeBuffer.tellp() > 1000 * 5000) {
_storage << _writeBuffer.rdbuf();
_writeBuffer.clear();
}
return s.getId();
}
// ____________________________________________________________________________
template <typename T>
void ShapeContainer<T>::open() {
_storage << _writeBuffer.rdbuf();
_writeBuffer.clear();
_ptr = 0;
_max = 0;
_storage.clear();
_storage.seekg(0, std::ios::beg);
}
// ____________________________________________________________________________
template <typename T>
bool ShapeContainer<T>::nextStoragePt(
ad::cppgtfs::gtfs::flat::ShapePoint* ret) {
while (_storage.good() && !_storage.fail()) {
if (!_ptr) {
_storage >> _curId;
_storage >> _max;
}
if (!_storage.good() || _storage.fail()) return false;
_storage >> ret->lat;
_storage >> ret->lng;
_storage >> ret->travelDist;
ret->seq = _ptr + 1;
ret->id = _curId;
if (_ptr + 1 == _max)
_ptr = 0;
else
_ptr++;
if (!_storage.good() || _storage.fail()) return false;
if (has(ret->id)) return true;
}
return false;
}
// ____________________________________________________________________________
template <typename T>
const std::string ShapeContainer<T>::getRef(const std::string& id) const {
if (!has(id)) return "";
return id;
}
// ____________________________________________________________________________
template <typename T>
std::string ShapeContainer<T>::getRef(const std::string& id) {
if (!has(id)) return "";
return id;
}

View file

@ -0,0 +1,71 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_STOPTIME_H_
#define PFAEDLE_GTFS_STOPTIME_H_
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include "ad/cppgtfs/gtfs/Stop.h"
#include "ad/cppgtfs/gtfs/StopTime.h"
#include "util/Misc.h"
using std::exception;
using std::string;
namespace pfaedle {
namespace gtfs {
template <typename StopT>
class StopTime {
public:
StopTime(const ad::cppgtfs::gtfs::Time& at, const ad::cppgtfs::gtfs::Time& dt,
typename StopT::Ref s, uint32_t seq, const std::string& hs,
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);
UNUSED(hs);
UNUSED(put);
UNUSED(dot);
UNUSED(distTrav);
UNUSED(isTp);
}
const typename StopT::Ref getStop() const { return _s; }
typename StopT::Ref getStop() { return _s; }
void setShapeDistanceTravelled(double d) { _dist = d; }
ad::cppgtfs::gtfs::Time getArrivalTime() const {
return ad::cppgtfs::gtfs::Time(0, 0, 0);
}
ad::cppgtfs::gtfs::Time getDepartureTime() const {
return ad::cppgtfs::gtfs::Time(0, 0, 0);
}
float getShapeDistanceTravelled() const { return _dist; }
uint16_t getSeq() const { return _sequence; }
private:
typename StopT::Ref _s;
uint32_t _sequence;
float _dist;
};
template <typename StopTimeT>
struct StopTimeCompare {
bool operator()(const StopTimeT& lh, const StopTimeT& rh) const {
return lh.getSeq() < rh.getSeq();
}
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_STOPTIME_H_

495
src/pfaedle/gtfs/Writer.cpp Normal file
View file

@ -0,0 +1,495 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <cstdio>
#include <fstream>
#include <map>
#include <string>
#include <utility>
#include "ad/cppgtfs/Parser.h"
#include "ad/cppgtfs/Writer.h"
#include "ad/cppgtfs/gtfs/flat/Agency.h"
#include "ad/util/CsvWriter.h"
#include "pfaedle/gtfs/Writer.h"
using ad::util::CsvWriter;
using ad::cppgtfs::Parser;
using pfaedle::gtfs::Writer;
// ____________________________________________________________________________
bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const {
std::ofstream fs;
std::ifstream is;
std::string gtfsPath(path);
std::string curFile;
std::string curFileTg;
curFile = gtfsPath + "/.agency.txt";
curFileTg = gtfsPath + "/agency.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeAgency(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
curFile = gtfsPath + "/.stops.txt";
curFileTg = gtfsPath + "/stops.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeStops(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
curFile = gtfsPath + "/.routes.txt";
curFileTg = gtfsPath + "/routes.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeRoutes(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
is.open((sourceFeed->getPath() + "/calendar.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.calendar.txt";
curFileTg = gtfsPath + "/calendar.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeCalendar(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.calendar_dates.txt";
curFileTg = gtfsPath + "/calendar_dates.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeCalendarDates(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/transfers.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.transfers.txt";
curFileTg = gtfsPath + "/transfers.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeTransfers(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.fare_attributes.txt";
curFileTg = gtfsPath + "/fare_attributes.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFares(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.open((sourceFeed->getPath() + "/fare_rules.txt").c_str());
if (is.good()) {
is.close();
curFile = gtfsPath + "/.fare_rules.txt";
curFileTg = gtfsPath + "/fare_rules.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFareRules(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.close();
curFile = gtfsPath + "/.shapes.txt";
curFileTg = gtfsPath + "/shapes.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeShapes(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
is.close();
curFile = gtfsPath + "/.trips.txt";
curFileTg = gtfsPath + "/trips.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
bool hasFreqs = writeTrips(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
is.open((sourceFeed->getPath() + "/frequencies.txt").c_str());
if (hasFreqs && is.good()) {
is.close();
curFile = gtfsPath + "/.frequencies.txt";
curFileTg = gtfsPath + "/frequencies.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFrequencies(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
is.close();
curFile = 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();
std::rename(curFile.c_str(), curFileTg.c_str());
if (!sourceFeed->getPublisherUrl().empty() &&
!sourceFeed->getPublisherName().empty()) {
curFile = gtfsPath + "/.feed_info.txt";
curFileTg = gtfsPath + "/feed_info.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeFeedInfo(sourceFeed, &fs);
fs.close();
std::rename(curFile.c_str(), curFileTg.c_str());
}
return true;
}
// ____________________________________________________________________________
bool Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const {
auto csvw = ad::cppgtfs::Writer::getFeedInfoCsvw(os);
csvw.flushLine();
csvw.writeString(f->getPublisherName());
csvw.writeString(f->getPublisherUrl());
csvw.writeString(f->getLang());
if (!f->getStartDate().empty())
csvw.writeInt(f->getStartDate().getYYYYMMDD());
else
csvw.skip();
if (!f->getEndDate().empty())
csvw.writeInt(f->getEndDate().getYYYYMMDD());
else
csvw.skip();
csvw.writeString(f->getVersion());
csvw.flushLine();
return true;
}
// ____________________________________________________________________________
bool Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/agency.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getAgencyCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Agency fa;
auto flds = Parser::getAgencyFlds(&csvp);
while (p.nextAgency(&csvp, &fa, flds)) {
w.writeAgency(fa, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/stops.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getStopsCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Stop s;
auto flds = Parser::getStopFlds(&csvp);
while (p.nextStop(&csvp, &s, flds)) {
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;
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);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/calendar.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getCalendarCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Calendar c;
auto flds = Parser::getCalendarFlds(&csvp);
while (p.nextCalendar(&csvp, &c, flds)) {
w.writeCalendar(c, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeCalendarDates(gtfs::Feed* sourceFeed,
std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getCalendarDatesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::CalendarDate c;
auto flds = Parser::getCalendarDateFlds(&csvp);
while (p.nextCalendarDate(&csvp, &c, flds)) {
w.writeCalendarDate(c, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/frequencies.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFrequencyCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Frequency f;
auto flds = Parser::getFrequencyFlds(&csvp);
while (p.nextFrequency(&csvp, &f, flds)) {
w.writeFrequency(f, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/transfers.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getTransfersCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Transfer t;
auto flds = Parser::getTransfersFlds(&csvp);
while (p.nextTransfer(&csvp, &t, flds)) {
w.writeTransfer(t, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFaresCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::Fare f;
auto flds = Parser::getFareFlds(&csvp);
while (p.nextFare(&csvp, &f, flds)) {
w.writeFare(f, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/fare_rules.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFareRulesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::FareRule f;
auto flds = Parser::getFareRuleFlds(&csvp);
while (p.nextFareRule(&csvp, &f, flds)) {
w.writeFareRule(f, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/shapes.txt").c_str());
CsvWriter csvw = ad::cppgtfs::Writer::getShapesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::ShapePoint sp;
ad::cppgtfs::Writer w;
if (fs.good()) {
CsvParser csvp(&fs);
Parser p;
auto flds = Parser::getShapeFlds(&csvp);
std::string curShapeId;
std::string curSkipShapeId;
while (p.nextShapePoint(&csvp, &sp, flds)) {
if (sp.id == curSkipShapeId) continue;
if (sp.id != curShapeId) {
if (sourceFeed->getShapes().has(sp.id)) {
curShapeId = sp.id;
} else {
curSkipShapeId = sp.id;
continue;
}
}
w.writeShapePoint(sp, &csvw);
}
}
sourceFeed->getShapes().open();
while (sourceFeed->getShapes().nextStoragePt(&sp)) {
w.writeShapePoint(sp, &csvw);
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeTrips(gtfs::Feed* sourceFeed, std::ostream* os) const {
ad::cppgtfs::Writer w;
bool hasFreqs = false;
CsvWriter csvw = ad::cppgtfs::Writer::getTripsCsvw(os);
csvw.flushLine();
for (auto t : sourceFeed->getTrips()) {
if (t.getFrequencies().size()) hasFreqs = true;
w.writeTrip(t.getFlat(), &csvw);
}
return hasFreqs;
}
// ____________________________________________________________________________
bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/stop_times.txt").c_str());
CsvParser csvp(&fs);
Parser p;
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getStopTimesCsvw(os);
csvw.flushLine();
ad::cppgtfs::gtfs::flat::StopTime st;
auto flds = Parser::getStopTimeFlds(&csvp);
std::string curTripId;
Trip* cur = 0;
while (p.nextStopTime(&csvp, &st, flds)) {
// we may have changed to distance field
if (curTripId != st.trip) {
cur = sourceFeed->getTrips().get(st.trip);
curTripId = st.trip;
}
for (const auto& stN : cur->getStopTimes()) {
if (stN.getSeq() == st.sequence)
st.shapeDistTravelled = stN.getShapeDistanceTravelled();
}
w.writeStopTime(st, &csvw);
}
fs.close();
return true;
}
// ___________________________________________________________________________
void Writer::cannotWrite(const std::string& file, const std::string& file2) {
std::stringstream ss;
ss << "(temporary file for " << file2 << ") Could not write to file";
throw ad::cppgtfs::WriterException(ss.str(), file);
}

41
src/pfaedle/gtfs/Writer.h Normal file
View 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 PFAEDLE_GTFS_WRITER_H_
#define PFAEDLE_GTFS_WRITER_H_
#include <string>
#include "Feed.h"
namespace pfaedle {
namespace gtfs {
class Writer {
public:
Writer() {}
bool 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;
bool writeTrips(Feed* f, std::ostream* os) const;
bool writeStopTimes(Feed* f, std::ostream* os) const;
static void cannotWrite(const std::string& file, const std::string& file2);
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_WRITER_H_

View file

@ -9,11 +9,12 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "ad/cppgtfs/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/gtfs/Feed.h"
#include "util/String.h" #include "util/String.h"
#include "util/geo/GeoGraph.h" #include "util/geo/GeoGraph.h"
using util::geograph::GeoEdgePL; using util::geograph::GeoEdgePL;
using ad::cppgtfs::gtfs::Trip; using pfaedle::gtfs::Trip;
namespace pfaedle { namespace pfaedle {
namespace netgraph { namespace netgraph {
@ -22,7 +23,7 @@ namespace netgraph {
* A payload class for edges on a network graph - that is a graph * A payload class for edges on a network graph - that is a graph
* that exactly represents a physical public transit network * that exactly represents a physical public transit network
*/ */
class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> { class EdgePL {
public: public:
EdgePL() {} EdgePL() {}
EdgePL(const LINE& l, const std::set<const Trip*>& trips) EdgePL(const LINE& l, const std::set<const Trip*>& trips)
@ -36,10 +37,10 @@ class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
util::json::Dict getAttrs() const { util::json::Dict getAttrs() const {
util::json::Dict obj; util::json::Dict obj;
obj["num_trips"] = static_cast<int>(_trips.size()); obj["num_trips"] = static_cast<int>(_trips.size());
obj["route_short_names"] = util::json::Array( obj["route_short_names"] =
_routeShortNames.begin(), _routeShortNames.end()); util::json::Array(_routeShortNames.begin(), _routeShortNames.end());
obj["trip_short_names"] = util::json::Array(_tripShortNames.begin(), obj["trip_short_names"] =
_tripShortNames.end()); util::json::Array(_tripShortNames.begin(), _tripShortNames.end());
return obj; return obj;
} }

View file

@ -12,7 +12,6 @@
using util::geograph::GeoNodePL; using util::geograph::GeoNodePL;
namespace pfaedle { namespace pfaedle {
namespace netgraph { namespace netgraph {
@ -20,15 +19,13 @@ namespace netgraph {
* A payload class for edges on a network graph - that is a graph * A payload class for edges on a network graph - that is a graph
* that exactly represents a physical public transit network * that exactly represents a physical public transit network
*/ */
class NodePL : public GeoNodePL<PFAEDLE_PRECISION> { class NodePL {
public: public:
NodePL() {} NodePL() {}
NodePL(const POINT& geom) { _geom = geom; } // NOLINT NodePL(const POINT& geom) { _geom = geom; } // NOLINT
const POINT* getGeom() const { return &_geom; } const POINT* getGeom() const { return &_geom; }
util::json::Dict getAttrs() const { util::json::Dict getAttrs() const { return util::json::Dict(); }
return util::json::Dict();
}
private: private:
POINT _geom; POINT _geom;

View file

@ -37,6 +37,31 @@ BOX BBoxIdx::getFullWebMercBox() const {
_root.box.getUpperRight().getY(), _root.box.getUpperRight().getX())); _root.box.getUpperRight().getY(), _root.box.getUpperRight().getX()));
} }
// _____________________________________________________________________________
BOX BBoxIdx::getFullBox() const { return _root.box; }
// _____________________________________________________________________________
std::vector<util::geo::Box<double>> BBoxIdx::getLeafs() const {
std::vector<util::geo::Box<double>> ret;
getLeafsRec(_root, &ret);
return ret;
}
// _____________________________________________________________________________
void BBoxIdx::getLeafsRec(const BBoxIdxNd& nd,
std::vector<util::geo::Box<double>>* ret) const {
if (!nd.childs.size()) {
ret->push_back(nd.box);
return;
}
for (const auto& child : nd.childs) {
getLeafsRec(child, ret);
}
return;
}
// _____________________________________________________________________________ // _____________________________________________________________________________
bool BBoxIdx::treeHas(const Point<double>& p, const BBoxIdxNd& nd) const { bool BBoxIdx::treeHas(const Point<double>& p, const BBoxIdxNd& nd) const {
if (!nd.childs.size()) return util::geo::contains(p, nd.box); if (!nd.childs.size()) return util::geo::contains(p, nd.box);

View file

@ -38,9 +38,15 @@ class BBoxIdx {
// Return the full total bounding box of this index // Return the full total bounding box of this index
BOX getFullWebMercBox() const; BOX getFullWebMercBox() const;
// Return the full total bounding box of this index
BOX getFullBox() const;
// Return the size of this index // Return the size of this index
size_t size() const; size_t size() const;
// return the leaf bounding boxes of this idx
std::vector<Box<double>> getLeafs() const;
private: private:
double _padding; double _padding;
size_t _size; size_t _size;
@ -50,6 +56,9 @@ class BBoxIdx {
void addToTree(const Box<double>& box, BBoxIdxNd* nd, size_t lvl); void addToTree(const Box<double>& box, BBoxIdxNd* nd, size_t lvl);
bool treeHas(const Point<double>& p, const BBoxIdxNd& nd) const; bool treeHas(const Point<double>& p, const BBoxIdxNd& nd) const;
void getLeafsRec(const BBoxIdxNd& nd,
std::vector<util::geo::Box<double>>* ret) const;
static const size_t MAX_LVL = 5; static const size_t MAX_LVL = 5;
static constexpr double MIN_COM_AREA = 0.0; static constexpr double MIN_COM_AREA = 0.0;
}; };

View file

@ -43,8 +43,20 @@ using pfaedle::osm::OsmRel;
using pfaedle::osm::OsmNode; using pfaedle::osm::OsmNode;
using pfaedle::osm::EdgeGrid; using pfaedle::osm::EdgeGrid;
using pfaedle::osm::NodeGrid; using pfaedle::osm::NodeGrid;
using pfaedle::osm::EqSearch;
using pfaedle::osm::BlockSearch;
using ad::cppgtfs::gtfs::Stop; using ad::cppgtfs::gtfs::Stop;
// _____________________________________________________________________________
bool EqSearch::operator()(const Node* cand, const StatInfo* si) const {
if (orphanSnap && cand->pl().getSI() &&
(!cand->pl().getSI()->getGroup() ||
cand->pl().getSI()->getGroup()->getStops().size() == 0)) {
return true;
}
return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi;
}
// _____________________________________________________________________________ // _____________________________________________________________________________
OsmBuilder::OsmBuilder() {} OsmBuilder::OsmBuilder() {}
@ -140,7 +152,8 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
POINT geom = *s->pl().getGeom(); POINT geom = *s->pl().getGeom();
NodePL pl = s->pl(); NodePL pl = s->pl();
pl.getSI()->setIsFromOsm(false); pl.getSI()->setIsFromOsm(false);
const auto& r = snapStation(g, &pl, &eg, &sng, opts, res, false, d); const auto& r =
snapStation(g, &pl, &eg, &sng, opts, res, false, false, d);
groupStats(r); groupStats(r);
for (auto n : r) { for (auto n : r) {
// if the snapped station is very near to the original OSM // if the snapped station is very near to the original OSM
@ -153,32 +166,70 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
} }
} }
std::vector<const Stop*> notSnapped;
for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) { for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) {
double d = opts.maxSnapDistances[i]; double d = opts.maxSnapDistances[i];
for (auto& s : *fs) { for (auto& s : *fs) {
auto pl = plFromGtfs(s.first, opts); auto pl = plFromGtfs(s.first, opts);
StatGroup* group = StatGroup* group = groupStats(
groupStats(snapStation(g, &pl, &eg, &sng, opts, res, snapStation(g, &pl, &eg, &sng, opts, res,
i == opts.maxSnapDistances.size() - 1, d)); i == opts.maxSnapDistances.size() - 1, false, d));
if (group) { if (group) {
group->addStop(s.first); group->addStop(s.first);
(*fs)[s.first] = *group->getNodes().begin(); (*fs)[s.first] = *group->getNodes().begin();
} else if (i == opts.maxSnapDistances.size() - 1) {
LOG(VDEBUG) << "Could not snap station "
<< "(" << pl.getSI()->getName() << ")"
<< " (" << s.first->getLat() << "," << s.first->getLng()
<< ") in normal run, trying again later in orphan mode.";
notSnapped.push_back(s.first);
}
}
}
if (notSnapped.size())
LOG(VDEBUG) << notSnapped.size() << " stations could not be snapped in "
"normal run, trying again in orphan "
"mode.";
// try again, but aggressively snap to orphan OSM stations which have
// not been assigned to any GTFS stop yet
for (size_t i = 0; i < opts.maxSnapDistances.size(); i++) {
double d = opts.maxSnapDistances[i];
for (auto& s : notSnapped) {
auto pl = plFromGtfs(s, opts);
StatGroup* group = groupStats(
snapStation(g, &pl, &eg, &sng, opts, res,
i == opts.maxSnapDistances.size() - 1, true, d));
if (group) {
group->addStop(s);
// add the added station name as an alt name to ensure future
// similarity
for (auto n : group->getNodes()) {
if (n->pl().getSI())
n->pl().getSI()->addAltName(pl.getSI()->getName());
}
(*fs)[s] = *group->getNodes().begin();
} else if (i == } else if (i ==
opts.maxSnapDistances.size() - 1) { // only fail on last opts.maxSnapDistances.size() - 1) { // only fail on last
// finally give up
// add a group with only this stop in it // add a group with only this stop in it
StatGroup* dummyGroup = new StatGroup(); StatGroup* dummyGroup = new StatGroup();
Node* dummyNode = g->addNd(pl); Node* dummyNode = g->addNd(pl);
dummyNode->pl().getSI()->setGroup(dummyGroup); dummyNode->pl().getSI()->setGroup(dummyGroup);
dummyGroup->addNode(dummyNode); dummyGroup->addNode(dummyNode);
dummyGroup->addStop(s.first); dummyGroup->addStop(s);
(*fs)[s.first] = dummyNode; (*fs)[s] = dummyNode;
LOG(WARN) << "Could not snap station " LOG(WARN) << "Could not snap station "
<< "(" << pl.getSI()->getName() << ")" << "(" << pl.getSI()->getName() << ")"
<< " (" << s.first->getLat() << "," << s.first->getLng() << " (" << s->getLat() << "," << s->getLng() << ")";
<< ")";
} }
} }
} }
@ -188,7 +239,7 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
deleteOrphNds(g); deleteOrphNds(g);
LOG(VDEBUG) << "Deleting orphan edges..."; LOG(VDEBUG) << "Deleting orphan edges...";
deleteOrphEdgs(g); deleteOrphEdgs(g, opts);
LOG(VDEBUG) << "Collapsing edges..."; LOG(VDEBUG) << "Collapsing edges...";
collapseEdges(g); collapseEdges(g);
@ -197,7 +248,7 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
deleteOrphNds(g); deleteOrphNds(g);
LOG(VDEBUG) << "Deleting orphan edges..."; LOG(VDEBUG) << "Deleting orphan edges...";
deleteOrphEdgs(g); deleteOrphEdgs(g, opts);
LOG(VDEBUG) << "Writing graph components..."; LOG(VDEBUG) << "Writing graph components...";
// the restrictor is needed here to prevent connections in the graph // the restrictor is needed here to prevent connections in the graph
@ -223,6 +274,89 @@ void OsmBuilder::read(const std::string& path, const OsmReadOpts& opts,
<< " edges and " << comps << " connected component(s)"; << " edges and " << comps << " connected component(s)";
} }
// _____________________________________________________________________________
void OsmBuilder::overpassQryWrite(std::ostream* out,
const std::vector<OsmReadOpts>& opts,
const BBoxIdx& latLngBox) const {
OsmIdSet bboxNodes, noHupNodes;
MultAttrMap emptyF;
RelLst rels;
OsmIdList ways;
RelMap nodeRels, wayRels;
// TODO(patrick): not needed here!
Restrictions rests;
NIdMap nodes;
// always empty
NIdMultMap multNodes;
util::xml::XmlWriter wr(out, true, 4);
*out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
wr.openComment();
wr.writeText(" - written by pfaedle -");
wr.closeTag();
wr.openTag("osm-script",
{{"timeout", "99999"}, {"element-limit", "1073741824"}});
OsmFilter filter;
for (const OsmReadOpts& o : opts) {
filter = filter.merge(OsmFilter(o.keepFilter, o.dropFilter));
}
wr.openTag("union");
size_t c = 0;
for (auto box : latLngBox.getLeafs()) {
if (box.getLowerLeft().getX() > box.getUpperRight().getX()) continue;
c++;
wr.openComment();
wr.writeText(std::string("Bounding box #") + std::to_string(c) + " (" +
std::to_string(box.getLowerLeft().getY()) + ", " +
std::to_string(box.getLowerLeft().getX()) + ", " +
std::to_string(box.getUpperRight().getY()) + ", " +
std::to_string(box.getUpperRight().getX()) + ")");
wr.closeTag();
for (auto t : std::vector<std::string>{"way", "node", "relation"}) {
for (auto r : filter.getKeepRules()) {
for (auto val : r.second) {
if (t == "way" && (val.second & OsmFilter::WAY)) continue;
if (t == "relation" && (val.second & OsmFilter::REL)) continue;
if (t == "node" && (val.second & OsmFilter::NODE)) continue;
wr.openTag("query", {{"type", t}});
if (val.first == "*")
wr.openTag("has-kv", {{"k", r.first}});
else
wr.openTag("has-kv", {{"k", r.first}, {"v", val.first}});
wr.closeTag();
wr.openTag("bbox-query",
{{"s", std::to_string(box.getLowerLeft().getY())},
{"w", std::to_string(box.getLowerLeft().getX())},
{"n", std::to_string(box.getUpperRight().getY())},
{"e", std::to_string(box.getUpperRight().getX())}});
wr.closeTag();
wr.closeTag();
}
}
}
}
wr.closeTag();
wr.openTag("union");
wr.openTag("item");
wr.closeTag();
wr.openTag("recurse", {{"type", "down"}});
wr.closeTag();
wr.closeTag();
wr.openTag("print");
wr.closeTags();
}
// _____________________________________________________________________________ // _____________________________________________________________________________
void OsmBuilder::filterWrite(const std::string& in, const std::string& out, void OsmBuilder::filterWrite(const std::string& in, const std::string& out,
const std::vector<OsmReadOpts>& opts, const std::vector<OsmReadOpts>& opts,
@ -250,8 +384,15 @@ void OsmBuilder::filterWrite(const std::string& in, const std::string& out,
outstr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; outstr << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
wr.openTag("osm"); wr.openTag("osm");
wr.openTag(
// TODO(patrick): write bounding box tag "bounds",
{{"minlat", std::to_string(latLngBox.getFullBox().getLowerLeft().getY())},
{"minlon", std::to_string(latLngBox.getFullBox().getLowerLeft().getX())},
{"maxlat",
std::to_string(latLngBox.getFullBox().getUpperRight().getY())},
{"maxlon",
std::to_string(latLngBox.getFullBox().getUpperRight().getX())}});
wr.closeTag();
OsmFilter filter; OsmFilter filter;
AttrKeySet attrKeys[3] = {}; AttrKeySet attrKeys[3] = {};
@ -1071,8 +1212,7 @@ EdgeGrid OsmBuilder::buildEdgeIdx(Graph* g, size_t size,
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size, NodeGrid OsmBuilder::buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox,
const BOX& webMercBox,
bool which) const { bool which) const {
NodeGrid ret(size, size, webMercBox, false); NodeGrid ret(size, size, webMercBox, false);
for (auto* n : *g->getNds()) { for (auto* n : *g->getNds()) {
@ -1158,9 +1298,10 @@ bool OsmBuilder::isBlocked(const Edge* e, const StatInfo* si, const POINT& p,
// _____________________________________________________________________________ // _____________________________________________________________________________
Node* OsmBuilder::eqStatReach(const Edge* e, const StatInfo* si, const POINT& p, Node* OsmBuilder::eqStatReach(const Edge* e, const StatInfo* si, const POINT& p,
double maxD, int maxFullTurns, double maxD, int maxFullTurns, double minAngle,
double minAngle) const { bool orphanSnap) const {
return depthSearch(e, si, p, maxD, maxFullTurns, minAngle, EqSearch()); return depthSearch(e, si, p, maxD, maxFullTurns, minAngle,
EqSearch(orphanSnap));
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
@ -1187,8 +1328,7 @@ std::set<Node*> OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng,
std::set<Node*> ret; std::set<Node*> ret;
double distor = webMercDistFactor(*s.getGeom()); double distor = webMercDistFactor(*s.getGeom());
std::set<Node*> neighs; std::set<Node*> neighs;
BOX box = BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
ng->get(box, &neighs); ng->get(box, &neighs);
for (auto* n : neighs) { for (auto* n : neighs) {
@ -1205,8 +1345,7 @@ std::set<Node*> OsmBuilder::getMatchingNds(const NodePL& s, NodeGrid* ng,
Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const { Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const {
double distor = webMercDistFactor(*s.getGeom()); double distor = webMercDistFactor(*s.getGeom());
std::set<Node*> neighs; std::set<Node*> neighs;
BOX box = BOX box = util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
util::geo::pad(util::geo::getBoundingBox(*s.getGeom()), d / distor);
ng->get(box, &neighs); ng->get(box, &neighs);
Node* ret = 0; Node* ret = 0;
@ -1229,7 +1368,7 @@ Node* OsmBuilder::getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const {
std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg, std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
NodeGrid* sng, const OsmReadOpts& opts, NodeGrid* sng, const OsmReadOpts& opts,
Restrictor* restor, bool surrHeur, Restrictor* restor, bool surrHeur,
double d) const { bool orphSnap, double d) const {
assert(s->getSI()); assert(s->getSI());
std::set<Node*> ret; std::set<Node*> ret;
@ -1239,10 +1378,14 @@ std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
if (pq.empty() && surrHeur) { if (pq.empty() && surrHeur) {
// no station found in the first round, try again with the nearest // no station found in the first round, try again with the nearest
// surrounding // surrounding station with matching name
// station with matching name
const Node* best = getMatchingNd(*s, sng, opts.maxSnapFallbackHeurDistance); const Node* best = getMatchingNd(*s, sng, opts.maxSnapFallbackHeurDistance);
if (best) getEdgCands(*best->pl().getGeom(), &pq, eg, d); if (best) {
getEdgCands(*best->pl().getGeom(), &pq, eg, d);
} else {
// if still no luck, get edge cands in fallback snap distance
getEdgCands(*s->getGeom(), &pq, eg, opts.maxSnapFallbackHeurDistance);
}
} }
while (!pq.empty()) { while (!pq.empty()) {
@ -1254,7 +1397,7 @@ std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
Node* eq = 0; Node* eq = 0;
if (!(eq = eqStatReach(e, s->getSI(), geom, 2 * d, 0, if (!(eq = eqStatReach(e, s->getSI(), geom, 2 * d, 0,
opts.maxAngleSnapReach))) { opts.maxAngleSnapReach, orphSnap))) {
if (e->pl().lvl() > opts.maxSnapLevel) continue; if (e->pl().lvl() > opts.maxSnapLevel) continue;
if (isBlocked(e, s->getSI(), geom, opts.maxBlockDistance, 0, if (isBlocked(e, s->getSI(), geom, opts.maxBlockDistance, 0,
opts.maxAngleSnapReach)) { opts.maxAngleSnapReach)) {
@ -1309,11 +1452,6 @@ std::set<Node*> OsmBuilder::snapStation(Graph* g, NodePL* s, EdgeGrid* eg,
} }
} }
// get surrounding nodes
// TODO(patrick): own distance configuration for this!
const auto& sur = getMatchingNds(*s, sng, opts.maxGroupSearchDistance);
ret.insert(sur.begin(), sur.end());
return ret; return ret;
} }
@ -1336,7 +1474,10 @@ StatGroup* OsmBuilder::groupStats(const NodeSet& s) const {
} }
} }
if (!used) delete ret; if (!used) {
delete ret;
return 0;
}
return ret; return ret;
} }
@ -1507,7 +1648,7 @@ void OsmBuilder::getKeptAttrKeys(const OsmReadOpts& opts,
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
void OsmBuilder::deleteOrphEdgs(Graph* g) const { void OsmBuilder::deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) const {
size_t ROUNDS = 3; size_t ROUNDS = 3;
for (size_t c = 0; c < ROUNDS; c++) { for (size_t c = 0; c < ROUNDS; c++) {
for (auto i = g->getNds()->begin(); i != g->getNds()->end();) { for (auto i = g->getNds()->begin(); i != g->getNds()->end();) {
@ -1515,6 +1656,15 @@ void OsmBuilder::deleteOrphEdgs(Graph* g) const {
++i; ++i;
continue; continue;
} }
// check if the removal of this edge would transform a steep angle
// full turn at an intersection into a node 2 eligible for contraction
// if so, dont delete
if (keepFullTurn(*i, opts.fullTurnAngle)) {
++i;
continue;
}
i = g->delNd(*i); i = g->delNd(*i);
continue; continue;
i++; i++;
@ -1706,3 +1856,43 @@ void OsmBuilder::writeSelfEdgs(Graph* g) const {
} }
} }
} }
// _____________________________________________________________________________
bool OsmBuilder::keepFullTurn(const trgraph::Node* n, double ang) const {
if (n->getInDeg() + n->getOutDeg() != 1) return false;
const trgraph::Edge* e = 0;
if (n->getOutDeg())
e = n->getAdjListOut().front();
else
e = n->getAdjListIn().front();
auto other = e->getOtherNd(n);
if (other->getInDeg() + other->getOutDeg() == 3) {
const trgraph::Edge* a = 0;
const trgraph::Edge* b = 0;
for (auto f : other->getAdjListIn()) {
if (f != e && !a)
a = f;
else if (f != e && !b)
b = f;
}
for (auto f : other->getAdjListOut()) {
if (f != e && !a)
a = f;
else if (f != e && !b)
b = f;
}
auto ap = a->pl().backHop();
auto bp = b->pl().backHop();
if (a->getTo() != other) ap = a->pl().frontHop();
if (b->getTo() != other) bp = b->pl().frontHop();
return router::angSmaller(ap, *other->pl().getGeom(), bp, ang);
}
return false;
}

View file

@ -59,10 +59,10 @@ struct SearchFunc {
}; };
struct EqSearch : public SearchFunc { struct EqSearch : public SearchFunc {
explicit EqSearch(bool orphanSnap) : orphanSnap(orphanSnap) {}
double minSimi = 0.9; double minSimi = 0.9;
bool operator()(const Node* cand, const StatInfo* si) const { bool orphanSnap;
return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi; bool operator()(const Node* cand, const StatInfo* si) const;
}
}; };
struct BlockSearch : public SearchFunc { struct BlockSearch : public SearchFunc {
@ -91,6 +91,11 @@ class OsmBuilder {
const BBoxIdx& box, size_t gridSize, router::FeedStops* fs, const BBoxIdx& box, size_t gridSize, router::FeedStops* fs,
Restrictor* res); Restrictor* res);
// Based on the list of options, output an overpass XML query for getting
// the data needed for routing
void overpassQryWrite(std::ostream* out, const std::vector<OsmReadOpts>& opts,
const BBoxIdx& latLngBox) const;
// Based on the list of options, read an OSM file from in and output an // Based on the list of options, read an OSM file from in and output an
// OSM file to out which contains exactly the entities that are needed // OSM file to out which contains exactly the entities that are needed
// from the file at in // from the file at in
@ -170,7 +175,7 @@ class OsmBuilder {
void writeGeoms(Graph* g) const; void writeGeoms(Graph* g) const;
void deleteOrphNds(Graph* g) const; void deleteOrphNds(Graph* g) const;
void deleteOrphEdgs(Graph* g) const; void deleteOrphEdgs(Graph* g, const OsmReadOpts& opts) const;
double dist(const Node* a, const Node* b) const; double dist(const Node* a, const Node* b) const;
double webMercDist(const Node* a, const Node* b) const; double webMercDist(const Node* a, const Node* b) const;
double webMercDistFactor(const POINT& a) const; double webMercDistFactor(const POINT& a) const;
@ -198,13 +203,14 @@ class OsmBuilder {
NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng, NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
const OsmReadOpts& opts, Restrictor* restor, bool surHeur, const OsmReadOpts& opts, Restrictor* restor, bool surHeur,
double maxD) const; bool orphSnap, double maxD) const;
// Checks if from the edge e, a station similar to si can be reach with less // 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 // than maxD distance and less or equal to "maxFullTurns" full turns. If
// such a station exists, it is returned. If not, 0 is returned. // such a station exists, it is returned. If not, 0 is returned.
Node* eqStatReach(const Edge* e, const StatInfo* si, const POINT& p, Node* eqStatReach(const Edge* e, const StatInfo* si, const POINT& p,
double maxD, int maxFullTurns, double maxAng) const; double maxD, int maxFullTurns, double maxAng,
bool orph) const;
Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p, Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p,
double maxD, int maxFullTurns, double minAngle, double maxD, int maxFullTurns, double minAngle,
@ -243,6 +249,8 @@ class OsmBuilder {
bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const; bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const;
bool keepFullTurn(const trgraph::Node* n, double ang) const;
std::map<TransitEdgeLine, TransitEdgeLine*> _lines; std::map<TransitEdgeLine, TransitEdgeLine*> _lines;
std::map<size_t, TransitEdgeLine*> _relLines; std::map<size_t, TransitEdgeLine*> _relLines;
}; };

View file

@ -102,12 +102,12 @@ uint64_t OsmFilter::contained(const AttrMap& attrs, const Attr& attr) {
// _____________________________________________________________________________ // _____________________________________________________________________________
uint8_t OsmFilter::level(const AttrMap& attrs) const { uint8_t OsmFilter::level(const AttrMap& attrs) const {
// the best matching level is always returned // the best matching level is always returned
for (int16_t i = 0; i < 7; i++) { for (int16_t i = 0; i < 8; i++) {
for (const auto& kv : attrs) { for (const auto& kv : attrs) {
const auto& lkv = (_levels + i)->find(kv.first); const auto& lkv = (_levels + i)->find(kv.first);
if (lkv != (_levels + i)->end()) { if (lkv != (_levels + i)->end()) {
for (const auto& val : lkv->second) { for (const auto& val : lkv->second) {
if (valMatches(kv.second, val.first)) return i + 1; if (valMatches(kv.second, val.first)) return i;
} }
} }
} }
@ -169,7 +169,7 @@ std::vector<std::string> OsmFilter::getAttrKeys() const {
for (const auto& kv : _noRestr) { for (const auto& kv : _noRestr) {
ret.push_back(kv.first); ret.push_back(kv.first);
} }
for (uint8_t i = 0; i < 7; i++) { for (uint8_t i = 0; i < 8; i++) {
for (const auto& kv : *(_levels + i)) { for (const auto& kv : *(_levels + i)) {
ret.push_back(kv.first); ret.push_back(kv.first);
} }
@ -191,27 +191,6 @@ OsmFilter OsmFilter::merge(const OsmFilter& other) const {
keep[kv.first].insert(kv.second.begin(), kv.second.end()); keep[kv.first].insert(kv.second.begin(), kv.second.end());
} }
// TODO(patrick): multi-level combination for filters. otherwise
// filter drop filters meant as a refinement for keep filters
// interfere with other keeps
// const auto* d = &_drop;
// for (size_t i = 0; i < 2; i++) {
// for (const auto& kv : *d) {
// if (keep.find(kv.first) != keep.end()) {
// for (const auto& val : kv.second) {
// if (keep[kv.first].find(val.first) == keep[kv.first].end()) {
// drop[kv.first].insert(val);
// }
// }
// } else {
// drop[kv.first].insert(kv.second.begin(), kv.second.end());
// }
// }
// d = &other._drop;
// }
return OsmFilter(keep, drop); return OsmFilter(keep, drop);
} }
@ -258,3 +237,13 @@ uint64_t OsmFilter::posRestr(const AttrMap& attrs) const {
if (contained(attrs, _noRestr, ALL)) return false; if (contained(attrs, _noRestr, ALL)) return false;
return (contained(attrs, _posRestr, ALL)); return (contained(attrs, _posRestr, ALL));
} }
// _____________________________________________________________________________
const pfaedle::osm::MultAttrMap& OsmFilter::getKeepRules() const {
return _keep;
}
// _____________________________________________________________________________
const pfaedle::osm::MultAttrMap& OsmFilter::getDropRules() const {
return _drop;
}

View file

@ -33,6 +33,9 @@ class OsmFilter {
OsmFilter merge(const OsmFilter& other) const; OsmFilter merge(const OsmFilter& other) const;
const MultAttrMap& getKeepRules() const;
const MultAttrMap& getDropRules() const;
std::string toString() const; std::string toString() const;
static bool valMatches(const std::string& a, const std::string& b, bool m); static bool valMatches(const std::string& a, const std::string& b, bool m);

View file

@ -113,7 +113,7 @@ struct OsmReadOpts {
MultAttrMap noHupFilter; MultAttrMap noHupFilter;
MultAttrMap keepFilter; MultAttrMap keepFilter;
MultAttrMap levelFilters[7]; MultAttrMap levelFilters[8];
MultAttrMap dropFilter; MultAttrMap dropFilter;
MultAttrMap oneWayFilter; MultAttrMap oneWayFilter;
MultAttrMap oneWayFilterRev; MultAttrMap oneWayFilterRev;
@ -136,7 +136,6 @@ struct OsmReadOpts {
double maxAngleSnapReach; double maxAngleSnapReach;
std::vector<double> maxSnapDistances; std::vector<double> maxSnapDistances;
double maxSnapFallbackHeurDistance; double maxSnapFallbackHeurDistance;
double maxGroupSearchDistance;
double maxBlockDistance; double maxBlockDistance;
double maxOsmStationDistance; double maxOsmStationDistance;
@ -144,6 +143,8 @@ struct OsmReadOpts {
// TODO(patrick): this is not implemented yet // TODO(patrick): this is not implemented yet
double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0}; double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0};
double fullTurnAngle;
// restriction system // restriction system
MultAttrMap restrPosRestr; MultAttrMap restrPosRestr;
MultAttrMap restrNegRestr; MultAttrMap restrNegRestr;
@ -179,7 +180,6 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
fabs(a.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 && fabs(a.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 &&
fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) < fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) <
0.1 && 0.1 &&
fabs(a.maxGroupSearchDistance - b.maxGroupSearchDistance) < 0.1 &&
fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 && fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 &&
fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 && fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 &&
fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 && fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 &&
@ -188,6 +188,7 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 && fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 &&
fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 && fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 &&
fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 && fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 &&
fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.1 &&
a.restrPosRestr == b.restrPosRestr && a.restrPosRestr == b.restrPosRestr &&
a.restrNegRestr == b.restrNegRestr && a.restrNegRestr == b.restrNegRestr &&
a.noRestrFilter == b.noRestrFilter; a.noRestrFilter == b.noRestrFilter;

View file

@ -60,11 +60,28 @@ inline double lineSimi(const std::string& a, const std::string& b) {
if (a.empty() || b.empty()) return 0; if (a.empty() || b.empty()) return 0;
// if one of the lines is completely contained in the other, return 1 if (a.size() > b.size() + 1) {
if (a.find(b) != std::string::npos) { // check if a begins with b
return 1; if (a.compare(0, b.size() + 1, b + " ") == 0) {
} else if (b.find(a) != std::string::npos) { return 1;
return 1; }
// check if a ends with b
if (a.compare(a.size() - (b.size() + 1), b.size() + 1, " " + b) == 0) {
return 1;
}
}
if (b.size() > a.size() + 1) {
// check if b begins with a
if (b.compare(0, a.size() + 1, a + " ") == 0) {
return 1;
}
// check if b ends with a
if (b.compare(b.size() - (a.size() + 1), a.size() + 1, " " + a) == 0) {
return 1;
}
} }
return 0; return 0;

View file

@ -82,21 +82,6 @@ util::json::Dict EdgePL::getAttrs() const {
obj["cost"] = std::to_string(_cost.getValue()); obj["cost"] = std::to_string(_cost.getValue());
obj["from_edge"] = util::toString(_startE); obj["from_edge"] = util::toString(_startE);
obj["to_edge"] = util::toString(_endE); obj["to_edge"] = util::toString(_endE);
obj["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
obj["cost_m_lvl0"] = std::to_string(_cost.meterDist);
obj["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
obj["cost_m_lvl2"] = std::to_string(_cost.meterDistLvl2);
obj["cost_m_lvl3"] = std::to_string(_cost.meterDistLvl3);
obj["cost_m_lvl4"] = std::to_string(_cost.meterDistLvl4);
obj["cost_m_lvl5"] = std::to_string(_cost.meterDistLvl5);
obj["cost_m_lvl6"] = std::to_string(_cost.meterDistLvl6);
obj["cost_m_lvl7"] = std::to_string(_cost.meterDistLvl7);
obj["cost_fullturn"] = std::to_string(_cost.fullTurns);
obj["cost_st_passthru"] = std::to_string(_cost.passThruStations);
obj["cost_m_oneway"] = std::to_string(_cost.oneWayMeters);
obj["cost_m_lineunmatch"] = std::to_string(_cost.lineUnmatchedMeters);
obj["cost_reach_node_pen"] = std::to_string(_cost.reachPen);
obj["cost_oneway_event"] = std::to_string(_cost.oneWayEdges);
obj["dummy"] = _edges.size() ? "no" : "yes"; obj["dummy"] = _edges.size() ? "no" : "yes";
return obj; return obj;

View file

@ -17,7 +17,7 @@ using util::geograph::GeoEdgePL;
namespace pfaedle { namespace pfaedle {
namespace router { namespace router {
class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> { class EdgePL {
public: public:
EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {} EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {}
const LINE* getGeom() const; const LINE* getGeom() const;

View file

@ -7,8 +7,8 @@
#include <set> #include <set>
#include <string> #include <string>
#include <vector>
#include <unordered_map> #include <unordered_map>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/Route.h" #include "ad/cppgtfs/gtfs/Route.h"
#include "pfaedle/trgraph/Graph.h" #include "pfaedle/trgraph/Graph.h"
@ -67,90 +67,35 @@ inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) {
} }
struct EdgeCost { struct EdgeCost {
EdgeCost() EdgeCost() : _cost(0) {}
: meterDist(0), explicit EdgeCost(double cost) : _cost(cost) {}
meterDistLvl1(0),
meterDistLvl2(0),
meterDistLvl3(0),
meterDistLvl4(0),
meterDistLvl5(0),
meterDistLvl6(0),
meterDistLvl7(0),
fullTurns(0),
passThruStations(0),
oneWayMeters(0),
oneWayEdges(0),
lineUnmatchedMeters(0),
reachPen(0),
o(0) {}
EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3, EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3,
double mDistLvl4, double mDistLvl5, double mDistLvl6, double mDistLvl4, double mDistLvl5, double mDistLvl6,
double mDistLvl7, uint32_t fullTurns, int32_t passThru, double mDistLvl7, uint32_t fullTurns, int32_t passThru,
double oneWayMeters, size_t oneWayEdges, double lineUnmatchedMeters, double oneWayMeters, size_t oneWayEdges, double lineUnmatchedMeters,
double reachPen, const RoutingOpts* o) double reachPen, const RoutingOpts* o) {
: meterDist(mDist), if (!o) {
meterDistLvl1(mDistLvl1), _cost = mDist + reachPen;
meterDistLvl2(mDistLvl2), } else {
meterDistLvl3(mDistLvl3), _cost = mDist * o->levelPunish[0] + mDistLvl1 * o->levelPunish[1] +
meterDistLvl4(mDistLvl4), mDistLvl2 * o->levelPunish[2] + mDistLvl3 * o->levelPunish[3] +
meterDistLvl5(mDistLvl5), mDistLvl4 * o->levelPunish[4] + mDistLvl5 * o->levelPunish[5] +
meterDistLvl6(mDistLvl6), mDistLvl6 * o->levelPunish[6] + mDistLvl7 * o->levelPunish[7] +
meterDistLvl7(mDistLvl7), oneWayMeters * o->oneWayPunishFac +
fullTurns(fullTurns), oneWayEdges * o->oneWayEdgePunish +
passThruStations(passThru), lineUnmatchedMeters * o->lineUnmatchedPunishFact +
oneWayMeters(oneWayMeters), fullTurns * o->fullTurnPunishFac +
oneWayEdges(oneWayEdges), passThru * o->passThruStationsPunish + reachPen;
lineUnmatchedMeters(lineUnmatchedMeters), }
reachPen(reachPen),
o(o) {}
double meterDist;
double meterDistLvl1;
double meterDistLvl2;
double meterDistLvl3;
double meterDistLvl4;
double meterDistLvl5;
double meterDistLvl6;
double meterDistLvl7;
uint32_t fullTurns;
int32_t passThruStations;
double oneWayMeters;
size_t oneWayEdges;
double lineUnmatchedMeters;
double reachPen;
const RoutingOpts* o;
double getValue() const {
if (!o) return meterDist + reachPen;
return meterDist * o->levelPunish[0] + meterDistLvl1 * o->levelPunish[1] +
meterDistLvl2 * o->levelPunish[2] +
meterDistLvl3 * o->levelPunish[3] +
meterDistLvl4 * o->levelPunish[4] +
meterDistLvl5 * o->levelPunish[5] +
meterDistLvl6 * o->levelPunish[6] +
meterDistLvl7 * o->levelPunish[7] +
oneWayMeters * o->oneWayPunishFac +
oneWayEdges * o->oneWayEdgePunish +
lineUnmatchedMeters * o->lineUnmatchedPunishFact +
fullTurns * o->fullTurnPunishFac +
passThruStations * o->passThruStationsPunish + reachPen;
} }
double getTotalMeters() const { float _cost;
return meterDist + meterDistLvl1 + meterDistLvl2 + meterDistLvl3 +
meterDistLvl4 + meterDistLvl5 + meterDistLvl6 + meterDistLvl7; double getValue() const { return _cost; }
}
}; };
inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) { inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) {
return EdgeCost( return EdgeCost(a.getValue() + b.getValue());
a.meterDist + b.meterDist, a.meterDistLvl1 + b.meterDistLvl1,
a.meterDistLvl2 + b.meterDistLvl2, a.meterDistLvl3 + b.meterDistLvl3,
a.meterDistLvl4 + b.meterDistLvl4, a.meterDistLvl5 + b.meterDistLvl5,
a.meterDistLvl6 + b.meterDistLvl6, a.meterDistLvl7 + b.meterDistLvl7,
a.fullTurns + b.fullTurns, a.passThruStations + b.passThruStations,
a.oneWayMeters + b.oneWayMeters, a.oneWayEdges + b.oneWayEdges,
a.lineUnmatchedMeters + b.lineUnmatchedMeters, a.reachPen + b.reachPen,
a.o ? a.o : b.o);
} }
inline bool operator<=(const EdgeCost& a, const EdgeCost& b) { inline bool operator<=(const EdgeCost& a, const EdgeCost& b) {
@ -165,9 +110,9 @@ inline bool operator>(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() > b.getValue(); return a.getValue() > b.getValue();
} }
template<typename F> template <typename F>
inline bool angSmaller(const Point<F>& f, const Point<F>& m, const Point<F>& t, inline bool angSmaller(const Point<F>& f, const Point<F>& m, const Point<F>& t,
double ang) { double ang) {
if (util::geo::innerProd(m, f, t) < ang) return 1; if (util::geo::innerProd(m, f, t) < ang) return 1;
return 0; return 0;
} }

View file

@ -18,7 +18,7 @@ using util::geograph::GeoNodePL;
namespace pfaedle { namespace pfaedle {
namespace router { namespace router {
class NodePL : public GeoNodePL<PFAEDLE_PRECISION> { class NodePL {
public: public:
NodePL() : _n(0) {} NodePL() : _n(0) {}
NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT

View file

@ -191,7 +191,8 @@ double CombCostFunc::operator()(const router::Edge* from, const router::Node* n,
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
Router::Router(size_t numThreads) : _cache(numThreads) { Router::Router(size_t numThreads, bool caching)
: _cache(numThreads), _caching(caching) {
for (size_t i = 0; i < numThreads; i++) { for (size_t i = 0; i < numThreads; i++) {
_cache[i] = new Cache(); _cache[i] = new Cache();
} }
@ -219,6 +220,9 @@ bool Router::compConned(const NodeCandGroup& a, const NodeCandGroup& b) const {
HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const { const osm::Restrictor& rest) const {
assert(a.size());
assert(b.size());
double pend = 0; double pend = 0;
for (size_t i = 0; i < a.size(); i++) { for (size_t i = 0; i < a.size(); i++) {
for (size_t j = 0; j < b.size(); j++) { for (size_t j = 0; j < b.size(); j++) {
@ -231,6 +235,7 @@ HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters"; LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters";
const trgraph::StatGroup* tgGrpTo = 0; const trgraph::StatGroup* tgGrpTo = 0;
if (b.begin()->nd->pl().getSI()) if (b.begin()->nd->pl().getSI())
tgGrpTo = b.begin()->nd->pl().getSI()->getGroup(); tgGrpTo = b.begin()->nd->pl().getSI()->getGroup();
@ -556,6 +561,7 @@ void Router::nestedCache(const EdgeList* el,
const std::set<trgraph::Edge*>& froms, const std::set<trgraph::Edge*>& froms,
const CostFunc& cost, const CostFunc& cost,
const RoutingAttrs& rAttrs) const { const RoutingAttrs& rAttrs) const {
if (!_caching) return;
if (el->size() == 0) return; if (el->size() == 0) return;
// iterate over result edges backwards // iterate over result edges backwards
EdgeList curEdges; EdgeList curEdges;
@ -586,7 +592,7 @@ std::set<pfaedle::trgraph::Edge*> Router::getCachedHops(
const RoutingAttrs& rAttrs) const { const RoutingAttrs& rAttrs) const {
std::set<trgraph::Edge*> ret; std::set<trgraph::Edge*> ret;
for (auto to : tos) { for (auto to : tos) {
if ((*_cache[omp_get_thread_num()])[rAttrs][from].count(to)) { if (_caching && (*_cache[omp_get_thread_num()])[rAttrs][from].count(to)) {
const auto& cv = (*_cache[omp_get_thread_num()])[rAttrs][from][to]; const auto& cv = (*_cache[omp_get_thread_num()])[rAttrs][from][to];
(*rCosts)[to] = cv.first; (*rCosts)[to] = cv.first;
*edgesRet.at(to) = cv.second; *edgesRet.at(to) = cv.second;
@ -601,6 +607,7 @@ std::set<pfaedle::trgraph::Edge*> Router::getCachedHops(
// _____________________________________________________________________________ // _____________________________________________________________________________
void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c, void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
EdgeList* edges, const RoutingAttrs& rAttrs) const { EdgeList* edges, const RoutingAttrs& rAttrs) const {
if (!_caching) return;
if (from == to) return; if (from == to) return;
(*_cache[omp_get_thread_num()])[rAttrs][from][to] = (*_cache[omp_get_thread_num()])[rAttrs][from][to] =
std::pair<EdgeCost, EdgeList>(c, *edges); std::pair<EdgeCost, EdgeList>(c, *edges);

View file

@ -137,7 +137,7 @@ struct CombCostFunc
class Router { class Router {
public: public:
// Init this router with caches for numThreads threads // Init this router with caches for numThreads threads
explicit Router(size_t numThreads); explicit Router(size_t numThreads, bool caching);
~Router(); ~Router();
// Find the most likely path through the graph for a node candidate route. // Find the most likely path through the graph for a node candidate route.
@ -163,6 +163,7 @@ class Router {
private: private:
mutable std::vector<Cache*> _cache; mutable std::vector<Cache*> _cache;
bool _caching;
HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const; const osm::Restrictor& rest) const;

View file

@ -9,6 +9,7 @@
#define omp_get_num_procs() 1 #define omp_get_num_procs() 1
#endif #endif
#include <exception>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
@ -16,6 +17,8 @@
#include "ad/cppgtfs/gtfs/Feed.h" #include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/Def.h" #include "pfaedle/Def.h"
#include "pfaedle/eval/Collector.h" #include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/gtfs/StopTime.h"
#include "pfaedle/osm/OsmBuilder.h" #include "pfaedle/osm/OsmBuilder.h"
#include "pfaedle/router/ShapeBuilder.h" #include "pfaedle/router/ShapeBuilder.h"
#include "pfaedle/trgraph/StatGroup.h" #include "pfaedle/trgraph/StatGroup.h"
@ -43,38 +46,36 @@ using pfaedle::router::EdgeListHops;
using pfaedle::router::Clusters; using pfaedle::router::Clusters;
using pfaedle::osm::BBoxIdx; using pfaedle::osm::BBoxIdx;
using ad::cppgtfs::gtfs::Stop; using ad::cppgtfs::gtfs::Stop;
using ad::cppgtfs::gtfs::Trip; using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Feed; using pfaedle::gtfs::Feed;
using ad::cppgtfs::gtfs::StopTime; using pfaedle::gtfs::StopTime;
using ad::cppgtfs::gtfs::ShapePoint; using ad::cppgtfs::gtfs::ShapePoint;
// _____________________________________________________________________________ // _____________________________________________________________________________
ShapeBuilder::ShapeBuilder(Feed* feed, MOTs mots, ShapeBuilder::ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed,
const config::MotConfig& motCfg, MOTs mots, const config::MotConfig& motCfg,
eval::Collector* ecoll, const config::Config& cfg) eval::Collector* ecoll, const config::Config& cfg)
: _feed(feed), : _feed(feed),
_evalFeed(evalFeed),
_mots(mots), _mots(mots),
_motCfg(motCfg), _motCfg(motCfg),
_ecoll(ecoll), _ecoll(ecoll),
_cfg(cfg), _cfg(cfg),
_crouter(omp_get_num_procs()), _crouter(omp_get_num_procs(), cfg.useCaching),
_curShpCnt(0) { _curShpCnt(0) {
_numThreads = _crouter.getCacheNumber(); _numThreads = _crouter.getCacheNumber();
writeMotStops(); writeMotStops();
// TODO(patrick): maybe do this on demand to avoid graph filtering / reading
// for input where no routing is necessary (already shape'd)
buildGraph(); buildGraph();
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
void ShapeBuilder::writeMotStops() { void ShapeBuilder::writeMotStops() {
for (auto t : _feed->getTrips()) { for (auto t : _feed->getTrips()) {
if (!_cfg.shapeTripId.empty() && t.second->getId() != _cfg.shapeTripId) if (!_cfg.shapeTripId.empty() && t.getId() != _cfg.shapeTripId) continue;
continue; if (_mots.count(t.getRoute()->getType()) &&
if (_mots.count(t.second->getRoute()->getType()) && _motCfg.mots.count(t.getRoute()->getType())) {
_motCfg.mots.count(t.second->getRoute()->getType())) { for (auto st : t.getStopTimes()) {
for (auto st : t.second->getStopTimes()) {
_stops[st.getStop()] = 0; _stops[st.getStop()] = 0;
} }
} }
@ -95,28 +96,34 @@ const NodeCandGroup& ShapeBuilder::getNodeCands(const Stop* s) const {
// _____________________________________________________________________________ // _____________________________________________________________________________
LINE ShapeBuilder::shapeL(const router::NodeCandRoute& ncr, LINE ShapeBuilder::shapeL(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs) { const router::RoutingAttrs& rAttrs) {
const router::EdgeListHops& res = route(ncr, rAttrs); try {
const router::EdgeListHops& res = route(ncr, rAttrs);
LINE l; LINE l;
for (const auto& hop : res) { for (const auto& hop : res) {
const trgraph::Node* last = hop.start; const trgraph::Node* last = hop.start;
if (hop.edges.size() == 0) { if (hop.edges.size() == 0) {
l.push_back(*hop.start->pl().getGeom()); l.push_back(*hop.start->pl().getGeom());
l.push_back(*hop.end->pl().getGeom()); l.push_back(*hop.end->pl().getGeom());
} }
for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) { for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) {
const auto* e = *i; const auto* e = *i;
if ((e->getFrom() == last) ^ e->pl().isRev()) { if ((e->getFrom() == last) ^ e->pl().isRev()) {
l.insert(l.end(), e->pl().getGeom()->begin(), e->pl().getGeom()->end()); l.insert(l.end(), e->pl().getGeom()->begin(),
} else { e->pl().getGeom()->end());
l.insert(l.end(), e->pl().getGeom()->rbegin(), } else {
e->pl().getGeom()->rend()); l.insert(l.end(), e->pl().getGeom()->rbegin(),
e->pl().getGeom()->rend());
}
last = e->getOtherNd(last);
} }
last = e->getOtherNd(last);
} }
}
return l; return l;
} catch (const std::runtime_error& e) {
LOG(ERROR) << e.what();
return LINE();
}
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
@ -193,9 +200,9 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
Clusters clusters = clusterTrips(_feed, _mots); Clusters clusters = clusterTrips(_feed, _mots);
LOG(DEBUG) << "Clustered trips into " << clusters.size() << " clusters."; LOG(DEBUG) << "Clustered trips into " << clusters.size() << " clusters.";
std::map<ad::cppgtfs::gtfs::Shape*, size_t> shpUsage; std::map<std::string, size_t> shpUsage;
for (auto t : _feed->getTrips()) { for (auto t : _feed->getTrips()) {
if (t.second->getShape()) shpUsage[t.second->getShape()]++; if (!t.getShape().empty()) shpUsage[t.getShape()]++;
} }
// to avoid unfair load balance on threads // to avoid unfair load balance on threads
@ -223,7 +230,7 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
<< "%, " << (EDijkstra::ITERS - oiters) << " iters, " << "%, " << (EDijkstra::ITERS - oiters) << " iters, "
/** /**
TODO: this is actually misleading. We are counting the TODO: this is actually misleading. We are counting the
Dijkstra iterations, but the measuring them against Dijkstra iterations, but measuring them against
the total running time (including all overhead + HMM solve) the total running time (including all overhead + HMM solve)
<< tput " << tput "
<< (static_cast<double>(EDijkstra::ITERS - oiters)) / << (static_cast<double>(EDijkstra::ITERS - oiters)) /
@ -249,7 +256,7 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
} }
std::vector<double> distances; std::vector<double> distances;
ad::cppgtfs::gtfs::Shape* shp = const ad::cppgtfs::gtfs::Shape& shp =
getGtfsShape(cshp, clusters[i][0], &distances); getGtfsShape(cshp, clusters[i][0], &distances);
LOG(DEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations."; LOG(DEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations.";
@ -259,14 +266,14 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
for (auto t : clusters[i]) { for (auto t : clusters[i]) {
if (_cfg.evaluate) { if (_cfg.evaluate) {
_ecoll->add(t, t->getShape(), shp, distances); _ecoll->add(t, _evalFeed->getShapes().get(t->getShape()), shp,
distances);
} }
if (t->getShape() && shpUsage[t->getShape()] > 0) { if (!t->getShape().empty() && shpUsage[t->getShape()] > 0) {
shpUsage[t->getShape()]--; shpUsage[t->getShape()]--;
if (shpUsage[t->getShape()] == 0) { if (shpUsage[t->getShape()] == 0) {
_feed->getShapes().remove(t->getShape()->getId()); _feed->getShapes().remove(t->getShape());
delete t->getShape();
} }
} }
setShape(t, shp, distances); setShape(t, shp, distances);
@ -295,27 +302,25 @@ void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
void ShapeBuilder::setShape(Trip* t, ad::cppgtfs::gtfs::Shape* s, void ShapeBuilder::setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s,
const std::vector<double>& distances) { const std::vector<double>& distances) {
assert(distances.size() == t->getStopTimes().size()); assert(distances.size() == t->getStopTimes().size());
// set distances // set distances
size_t i = 0; size_t i = 0;
for (const StopTime& st : t->getStopTimes()) { for (const auto& st : t->getStopTimes()) {
const_cast<StopTime&>(st).setShapeDistanceTravelled(distances[i]); const_cast<StopTime<Stop>&>(st).setShapeDistanceTravelled(distances[i]);
i++; i++;
} }
t->setShape(s);
std::lock_guard<std::mutex> guard(_shpMutex); std::lock_guard<std::mutex> guard(_shpMutex);
_feed->getShapes().add(s); // TODO(patrick):
t->setShape(_feed->getShapes().add(s));
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape( ad::cppgtfs::gtfs::Shape ShapeBuilder::getGtfsShape(
const Shape& shp, Trip* t, std::vector<double>* hopDists) { const Shape& shp, Trip* t, std::vector<double>* hopDists) {
ad::cppgtfs::gtfs::Shape* ret = ad::cppgtfs::gtfs::Shape ret(getFreeShapeId(t));
new ad::cppgtfs::gtfs::Shape(getFreeShapeId(t));
assert(shp.hops.size() == t->getStopTimes().size() - 1); assert(shp.hops.size() == t->getStopTimes().size() - 1);
@ -338,7 +343,7 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
last = *hop.start->pl().getGeom(); last = *hop.start->pl().getGeom();
if (dist - lastDist > 0.01) { if (dist - lastDist > 0.01) {
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
seq++; seq++;
lastDist = dist; lastDist = dist;
} }
@ -349,11 +354,12 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
if (dist - lastDist > 0.01) { if (dist - lastDist > 0.01) {
ll = webMercToLatLng<PFAEDLE_PRECISION>( ll = webMercToLatLng<PFAEDLE_PRECISION>(
hop.end->pl().getGeom()->getX(), hop.end->pl().getGeom()->getY()); hop.end->pl().getGeom()->getX(), hop.end->pl().getGeom()->getY());
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
seq++; seq++;
lastDist = dist; lastDist = dist;
} }
} }
for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) { for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) {
const auto* e = *i; const auto* e = *i;
if ((e->getFrom() == l) ^ e->pl().isRev()) { if ((e->getFrom() == l) ^ e->pl().isRev()) {
@ -367,7 +373,7 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
if (dist - lastDist > 0.01) { if (dist - lastDist > 0.01) {
POINT ll = POINT ll =
webMercToLatLng<PFAEDLE_PRECISION>(cur.getX(), cur.getY()); webMercToLatLng<PFAEDLE_PRECISION>(cur.getX(), cur.getY());
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
seq++; seq++;
lastDist = dist; lastDist = dist;
} }
@ -383,7 +389,7 @@ ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
if (dist - lastDist > 0.01) { if (dist - lastDist > 0.01) {
POINT ll = POINT ll =
webMercToLatLng<PFAEDLE_PRECISION>(cur.getX(), cur.getY()); webMercToLatLng<PFAEDLE_PRECISION>(cur.getX(), cur.getY());
ret->addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq)); ret.addPoint(ShapePoint(ll.getY(), ll.getX(), dist, seq));
seq++; seq++;
lastDist = dist; lastDist = dist;
} }
@ -447,33 +453,30 @@ const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) const {
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
BBoxIdx ShapeBuilder::getPaddedGtfsBox(const Feed* feed, double pad, void ShapeBuilder::getGtfsBox(const Feed* feed, const MOTs& mots,
const MOTs& mots, const std::string& tid, const std::string& tid, bool dropShapes,
bool dropShapes) { osm::BBoxIdx* box) {
osm::BBoxIdx box(pad);
for (const auto& t : feed->getTrips()) { for (const auto& t : feed->getTrips()) {
if (!tid.empty() && t.second->getId() != tid) continue; if (!tid.empty() && t.getId() != tid) continue;
if (tid.empty() && t.second->getShape() && !dropShapes) continue; if (tid.empty() && !t.getShape().empty() && !dropShapes) continue;
if (t.second->getStopTimes().size() < 2) continue; if (t.getStopTimes().size() < 2) continue;
if (mots.count(t.second->getRoute()->getType())) { if (mots.count(t.getRoute()->getType())) {
DBox cur; DBox cur;
for (const auto& st : t.second->getStopTimes()) { for (const auto& st : t.getStopTimes()) {
cur = extendBox(DPoint(st.getStop()->getLng(), st.getStop()->getLat()), cur = extendBox(DPoint(st.getStop()->getLng(), st.getStop()->getLat()),
cur); cur);
} }
box.add(cur); box->add(cur);
} }
} }
return box;
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
void ShapeBuilder::buildGraph() { void ShapeBuilder::buildGraph() {
osm::OsmBuilder osmBuilder; osm::OsmBuilder osmBuilder;
osm::BBoxIdx box = osm::BBoxIdx box(BOX_PADDING);
getPaddedGtfsBox(_feed, 2500, _mots, _cfg.shapeTripId, _cfg.dropShapes); getGtfsBox(_feed, _mots, _cfg.shapeTripId, _cfg.dropShapes, &box);
osmBuilder.read(_cfg.osmPath, _motCfg.osmBuildOpts, &_g, box, _cfg.gridSize, osmBuilder.read(_cfg.osmPath, _motCfg.osmBuildOpts, &_g, box, _cfg.gridSize,
getFeedStops(), &_restr); getFeedStops(), &_restr);
@ -497,6 +500,11 @@ NodeCandRoute ShapeBuilder::getNCR(Trip* trip) const {
for (const auto& st : trip->getStopTimes()) { for (const auto& st : trip->getStopTimes()) {
ncr[i] = getNodeCands(st.getStop()); ncr[i] = getNodeCands(st.getStop());
if (ncr[i].size() == 0) {
throw std::runtime_error("No node candidate found for station '" +
st.getStop()->getName() + "' on trip '" +
trip->getId() + "'");
}
i++; i++;
} }
return ncr; return ncr;
@ -535,29 +543,29 @@ Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) {
size_t j = 0; size_t j = 0;
Clusters ret; Clusters ret;
for (const auto& trip : f->getTrips()) { for (auto& trip : f->getTrips()) {
if (trip.second->getShape() && !_cfg.dropShapes) continue; if (!trip.getShape().empty() && !_cfg.dropShapes) continue;
if (trip.second->getStopTimes().size() < 2) continue; if (trip.getStopTimes().size() < 2) continue;
if (!mots.count(trip.second->getRoute()->getType()) || if (!mots.count(trip.getRoute()->getType()) ||
!_motCfg.mots.count(trip.second->getRoute()->getType())) !_motCfg.mots.count(trip.getRoute()->getType()))
continue; continue;
bool found = false; bool found = false;
auto spair = StopPair(trip.second->getStopTimes().begin()->getStop(), auto spair = StopPair(trip.getStopTimes().begin()->getStop(),
trip.second->getStopTimes().rbegin()->getStop()); trip.getStopTimes().rbegin()->getStop());
const auto& c = clusterIdx[spair]; const auto& c = clusterIdx[spair];
for (size_t i = 0; i < c.size(); i++) { for (size_t i = 0; i < c.size(); i++) {
j++; j++;
if (routingEqual(ret[c[i]][0], trip.second)) { if (routingEqual(ret[c[i]][0], &trip)) {
ret[c[i]].push_back(trip.second); ret[c[i]].push_back(&trip);
found = true; found = true;
break; break;
} }
} }
if (!found) { if (!found) {
ret.push_back({trip.second}); ret.push_back(Cluster{&trip});
// explicit call to write render attrs to cache // explicit call to write render attrs to cache
getRAttrs(trip.second); getRAttrs(&trip);
clusterIdx[spair].push_back(ret.size() - 1); clusterIdx[spair].push_back(ret.size() - 1);
} }
} }

View file

@ -16,6 +16,7 @@
#include "pfaedle/config/MotConfig.h" #include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/PfaedleConfig.h" #include "pfaedle/config/PfaedleConfig.h"
#include "pfaedle/eval/Collector.h" #include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/netgraph/Graph.h" #include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/Restrictor.h" #include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Misc.h" #include "pfaedle/router/Misc.h"
@ -27,8 +28,8 @@ namespace pfaedle {
namespace router { namespace router {
using ad::cppgtfs::gtfs::Stop; using ad::cppgtfs::gtfs::Stop;
using ad::cppgtfs::gtfs::Trip; using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Feed; using pfaedle::gtfs::Feed;
struct Shape { struct Shape {
router::EdgeListHops hops; router::EdgeListHops hops;
@ -48,8 +49,9 @@ typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
*/ */
class ShapeBuilder { class ShapeBuilder {
public: public:
ShapeBuilder(Feed* feed, MOTs mots, const config::MotConfig& motCfg, ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots,
eval::Collector* ecoll, const config::Config& cfg); const config::MotConfig& motCfg, eval::Collector* ecoll,
const config::Config& cfg);
void shape(pfaedle::netgraph::Graph* ng); void shape(pfaedle::netgraph::Graph* ng);
@ -66,12 +68,13 @@ class ShapeBuilder {
const trgraph::Graph* getGraph() const; const trgraph::Graph* getGraph() const;
static osm::BBoxIdx getPaddedGtfsBox(const Feed* feed, double pad, static void getGtfsBox(const Feed* feed, const MOTs& mots,
const MOTs& mots, const std::string& tid, const std::string& tid, bool dropShapes,
bool dropShapes); osm::BBoxIdx* box);
private: private:
Feed* _feed; Feed* _feed;
ad::cppgtfs::gtfs::Feed* _evalFeed;
MOTs _mots; MOTs _mots;
config::MotConfig _motCfg; config::MotConfig _motCfg;
eval::Collector* _ecoll; eval::Collector* _ecoll;
@ -101,10 +104,10 @@ class ShapeBuilder {
std::string getFreeShapeId(Trip* t); std::string getFreeShapeId(Trip* t);
ad::cppgtfs::gtfs::Shape* getGtfsShape(const Shape& shp, Trip* t, ad::cppgtfs::gtfs::Shape getGtfsShape(const Shape& shp, Trip* t,
std::vector<double>* hopDists); std::vector<double>* hopDists);
void setShape(Trip* t, ad::cppgtfs::gtfs::Shape* s, void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s,
const std::vector<double>& dists); const std::vector<double>& dists);
router::NodeCandRoute getNCR(Trip* trip) const; router::NodeCandRoute getNCR(Trip* trip) const;

View file

@ -39,7 +39,7 @@ EdgePL::EdgePL(const EdgePL& pl, bool geoflat)
} }
_flines[_l]++; _flines[_l]++;
for (auto l : _lines) addLine(l); for (auto l : pl._lines) addLine(l);
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
@ -82,7 +82,9 @@ double EdgePL::getLength() const { return _length; }
// _____________________________________________________________________________ // _____________________________________________________________________________
void EdgePL::addLine(const TransitEdgeLine* l) { void EdgePL::addLine(const TransitEdgeLine* l) {
if (_lines.insert(l).second) { if (std::find(_lines.begin(), _lines.end(), l) == _lines.end()) {
_lines.reserve(_lines.size() + 1);
_lines.push_back(l);
if (_tlines.count(l)) if (_tlines.count(l))
_tlines[l]++; _tlines[l]++;
else else
@ -96,7 +98,7 @@ void EdgePL::addLines(const std::vector<TransitEdgeLine*>& l) {
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
const std::set<const TransitEdgeLine*>& EdgePL::getLines() const { const std::vector<const TransitEdgeLine*>& EdgePL::getLines() const {
return _lines; return _lines;
} }

View file

@ -45,7 +45,7 @@ inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) {
/* /*
* An edge payload class for the transit graph. * An edge payload class for the transit graph.
*/ */
class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> { class EdgePL {
public: public:
EdgePL(); EdgePL();
~EdgePL(); ~EdgePL();
@ -101,7 +101,7 @@ class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
void addLines(const std::vector<TransitEdgeLine*>& l); void addLines(const std::vector<TransitEdgeLine*>& l);
// Return the TransitEdgeLines stored for this payload // Return the TransitEdgeLines stored for this payload
const std::set<const TransitEdgeLine*>& getLines() const; const std::vector<const TransitEdgeLine*>& getLines() const;
// Returns the last hop of the payload - this is the (n-2)th point in // Returns the last hop of the payload - this is the (n-2)th point in
// the payload geometry of length n > 1 // the payload geometry of length n > 1
@ -123,7 +123,7 @@ class EdgePL : public GeoEdgePL<PFAEDLE_PRECISION> {
LINE* _l; LINE* _l;
std::set<const TransitEdgeLine*> _lines; std::vector<const TransitEdgeLine*> _lines;
static void unRefTLine(const TransitEdgeLine* l); static void unRefTLine(const TransitEdgeLine* l);

View file

@ -26,7 +26,7 @@ struct Component {
/* /*
* A node payload class for the transit graph. * A node payload class for the transit graph.
*/ */
class NodePL : public GeoNodePL<PFAEDLE_PRECISION> { class NodePL {
public: public:
NodePL(); NodePL();
NodePL(const NodePL& pl); // NOLINT NodePL(const NodePL& pl); // NOLINT

View file

@ -2,4 +2,11 @@ file(GLOB_RECURSE util_SRC *.cpp)
list(REMOVE_ITEM util_SRC TestMain.cpp) list(REMOVE_ITEM util_SRC TestMain.cpp)
add_library(util ${util_SRC}) add_library(util ${util_SRC})
find_package( ZLIB )
if (ZLIB_FOUND)
include_directories( ${ZLIB_INCLUDE_DIRS} )
target_link_libraries( util ${ZLIB_LIBRARIES} )
add_definitions( -DZLIB_FOUND=${ZLIB_FOUND} )
endif( ZLIB_FOUND )
add_subdirectory(tests) add_subdirectory(tests)

View file

@ -26,7 +26,7 @@ class Nullable {
: val(other.val), null(other.isNull()) {} : val(other.val), null(other.isNull()) {}
Nullable& operator=(const Nullable& other) { Nullable& operator=(const Nullable& other) {
val = other.get(); if (!other.isNull()) val = other.get();
null = other.isNull(); null = other.isNull();
return *this; return *this;
} }

View file

@ -142,6 +142,39 @@ inline size_t editDist(const std::string& s1, const std::string& s2) {
return prev[len2]; return prev[len2];
} }
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const std::string& s,
size_t deltaMax) {
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
size_t len1 = prefix.size();
size_t len2 = std::min(s.size(), prefix.size() + deltaMax + 1);
std::vector<size_t> d((len1 + 1) * (len2 + 1));
d[0] = 0;
for (size_t i = 1; i <= len1; ++i) d[i * (len2 + 1)] = i;
for (size_t i = 1; i <= len2; ++i) d[ i] = i;
for (size_t i = 1; i <= len1; i++) {
for (size_t j = 1; j <= len2; j++) {
d[i * (len2 + 1) + j] = std::min(std::min(d[(i - 1) * (len2 + 1) + j] + 1, d[i * (len2 + 1) + j - 1] + 1),
d[(i - 1) * (len2 + 1) + j - 1] + (prefix[i - 1] == s[j - 1] ? 0 : 1));
}
}
// take min of last row
size_t deltaMin = std::max(std::max(deltaMax + 1, prefix.size()), s.size());
for (size_t i = 0; i <= len2; i++) {
if (d[len1 * (len2 + 1) + i] < deltaMin) deltaMin = d[len1 * (len2 + 1) + i];
}
return deltaMin;
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const std::string& s) {
return prefixEditDist(prefix, s, s.size());
}
// _____________________________________________________________________________ // _____________________________________________________________________________
template <class Iter> template <class Iter>
inline std::string implode(Iter begin, const Iter& end, const char* del) { inline std::string implode(Iter begin, const Iter& end, const char* del) {

View file

@ -47,7 +47,7 @@ typedef Polygon<double> DPolygon;
typedef Polygon<float> FPolygon; typedef Polygon<float> FPolygon;
typedef Polygon<int> IPolygon; typedef Polygon<int> IPolygon;
const static double EPSILON = 0.00000000001; const static double EPSILON = 0.00001;
const static double RAD = 0.017453292519943295; // PI/180 const static double RAD = 0.017453292519943295; // PI/180
// _____________________________________________________________________________ // _____________________________________________________________________________
@ -236,7 +236,7 @@ inline RotatedBox<T> shrink(const RotatedBox<T>& b, double d) {
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
inline bool doubleEq(double a, double b) { return fabs(a - b) < 0.000001; } inline bool doubleEq(double a, double b) { return fabs(a - b) < EPSILON; }
// _____________________________________________________________________________ // _____________________________________________________________________________
template <typename T> template <typename T>
@ -404,7 +404,7 @@ inline bool intersects(const LineSegment<T>& ls1, const LineSegment<T>& ls2) {
// intersecting // intersecting
return intersects(getBoundingBox(ls1), getBoundingBox(ls2)) && return intersects(getBoundingBox(ls1), getBoundingBox(ls2)) &&
(((contains(ls1.first, ls2) ^ contains(ls1.second, ls2)) ^ (((contains(ls1.first, ls2) ^ contains(ls1.second, ls2)) ^
(contains(ls2.first, ls1) ^ contains(ls2.second, ls1))) || (contains(ls2.first, ls1) ^ contains(ls2.second, ls1))) ||
(((crossProd(ls1.first, ls2) < 0) ^ (((crossProd(ls1.first, ls2) < 0) ^
(crossProd(ls1.second, ls2) < 0)) && (crossProd(ls1.second, ls2) < 0)) &&
((crossProd(ls2.first, ls1) < 0) ^ ((crossProd(ls2.first, ls1) < 0) ^
@ -1153,7 +1153,7 @@ inline size_t convexHullImpl(const MultiPoint<T>& a, size_t p1, size_t p2,
for (const auto& p : a) { for (const auto& p : a) {
double tmpDist = distToSegment((*h)[p1], (*h)[p2], p); double tmpDist = distToSegment((*h)[p1], (*h)[p2], p);
double cp = crossProd(p, LineSegment<T>((*h)[p1], (*h)[p2])); double cp = crossProd(p, LineSegment<T>((*h)[p1], (*h)[p2]));
if (((cp > 0 && !d) || (cp < 0 && d)) && tmpDist > maxDist) { if (((cp > 0 && !d) || (cp < 0 && d)) && tmpDist >= maxDist + EPSILON) {
pa = p; pa = p;
found = true; found = true;
maxDist = tmpDist; maxDist = tmpDist;
@ -1462,7 +1462,8 @@ inline Point<T> latLngToWebMerc(double lat, double lng) {
// _____________________________________________________________________________ // _____________________________________________________________________________
template <typename T> template <typename T>
inline Point<T> webMercToLatLng(double x, double y) { inline Point<T> webMercToLatLng(double x, double y) {
double lat = 114.591559026 * (atan(exp(y / 6378137.0)) - 0.78539825); double lat =
(1.5707963267948966 - (2.0 * atan(exp(-y / 6378137.0)))) * (180.0 / M_PI);
double lon = x / 111319.4907932735677; double lon = x / 111319.4907932735677;
return Point<T>(lon, lat); return Point<T>(lon, lat);
} }

View file

@ -5,6 +5,8 @@
#ifndef UTIL_GEO_POINT_H_ #ifndef UTIL_GEO_POINT_H_
#define UTIL_GEO_POINT_H_ #define UTIL_GEO_POINT_H_
#include <vector>
namespace util { namespace util {
namespace geo { namespace geo {

View file

@ -0,0 +1,33 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GRAPH_ALGORITHM_H_
#define UTIL_GRAPH_ALGORITHM_H_
#include <stack>
#include "util/graph/Edge.h"
#include "util/graph/UndirGraph.h"
#include "util/graph/Node.h"
namespace util {
namespace graph {
using util::graph::Graph;
using util::graph::Node;
using util::graph::Edge;
// collection of general graph algorithms
class Algorithm {
public:
template <typename N, typename E>
static std::vector<std::set<Node<N, E>*> > connectedComponents(
const UndirGraph<N, E>& g);
};
#include "util/graph/Algorithm.tpp"
}
}
#endif // UTIL_GRAPH_ALGORITHM_H_

View file

@ -0,0 +1,32 @@
// 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) {
std::vector<std::set<Node<N, E>*>> ret;
std::set<Node<N, E>*> visited;
for (auto* n : g.getNds()) {
if (!visited.count(n)) {
ret.resize(ret.size() + 1);
std::stack<Node<N, E>*> q;
q.push(n);
while (!q.empty()) {
Node<N, E>* cur = q.top();
q.pop();
ret.back().insert(cur);
visited.insert(cur);
for (auto* e : cur->getAdjList()) {
if (!visited.count(e->getOtherNd(cur))) q.push(e->getOtherNd(cur));
}
}
}
}
return ret;
}

View file

@ -6,6 +6,7 @@
#define UTIL_GRAPH_DIRNODE_H_ #define UTIL_GRAPH_DIRNODE_H_
#include <vector> #include <vector>
#include <algorithm>
#include "util/graph/Node.h" #include "util/graph/Node.h"
namespace util { namespace util {

View file

@ -33,12 +33,15 @@ class Node {
virtual void addEdge(Edge<N, E>* e) = 0; virtual void addEdge(Edge<N, E>* e) = 0;
virtual void removeEdge(Edge<N, E>* e) = 0; virtual void removeEdge(Edge<N, E>* e) = 0;
virtual ~Node() {}; virtual ~Node() = 0;
virtual N& pl() = 0; virtual N& pl() = 0;
virtual const N& pl() const = 0; virtual const N& pl() const = 0;
}; };
template <typename N, typename E>
inline Node<N, E>::~Node() {}
}} }}
#endif // UTIL_GRAPH_NODE_H_ #endif // UTIL_GRAPH_NODE_H_

View file

@ -6,6 +6,7 @@
#define UTIL_GRAPH_UNDIRNODE_H_ #define UTIL_GRAPH_UNDIRNODE_H_
#include <vector> #include <vector>
#include <algorithm>
#include "util/graph/Node.h" #include "util/graph/Node.h"
namespace util { namespace util {

347
src/util/http/Server.cpp Normal file
View file

@ -0,0 +1,347 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef ZLIB_CONST
#define ZLIB_CONST
#endif
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <algorithm>
#include <memory>
#include <sstream>
#include <thread>
#include <unordered_map>
#ifdef ZLIB_FOUND
#include <zlib.h>
#endif
#include <vector>
#include "Server.h"
#include "util/String.h"
using util::http::Socket;
using util::http::Queue;
using util::http::Req;
using util::http::HttpErr;
using util::http::HttpServer;
using util::http::HeaderState;
// _____________________________________________________________________________
Socket::Socket(int port) {
int y = 1;
_sock = socket(PF_INET, SOCK_STREAM, 0);
if (_sock < 0)
throw std::runtime_error(std::string("Could not create socket (") +
std::strerror(errno) + ")");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
memset(&(addr.sin_zero), '\0', 8);
setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y));
// https://news.ycombinator.com/item?id=10608356
setsockopt(_sock, IPPROTO_TCP, TCP_QUICKACK, &y, sizeof(y));
if (bind(_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
throw std::runtime_error(std::string("Could not bind to port ") +
std::to_string(port) + " (" +
std::strerror(errno) + ")");
}
}
// _____________________________________________________________________________
Socket::~Socket() { close(_sock); }
// _____________________________________________________________________________
int Socket::wait() {
if (listen(_sock, BLOG) < 0)
throw std::runtime_error(std::string("Cannot listen to socket (") +
std::strerror(errno) + ")");
sockaddr_in cli_addr;
socklen_t clilen = sizeof(cli_addr);
int sock = accept(_sock, reinterpret_cast<sockaddr*>(&cli_addr), &clilen);
return sock;
}
// _____________________________________________________________________________
void HttpServer::send(int sock, Answer* aw) {
std::string enc = "identity";
if (aw->gzip) aw->pl = compress(aw->pl, &enc);
aw->params["Content-Encoding"] = enc;
aw->params["Content-Length"] = std::to_string(aw->pl.size());
std::stringstream ss;
ss << "HTTP/1.1 " << aw->status << "\r\n";
for (const auto& kv : aw->params)
ss << kv.first << ": " << kv.second << "\r\n";
ss << "\r\n" << aw->pl;
std::string buff = ss.str();
size_t writes = 0;
// https://news.ycombinator.com/item?id=10608356
int y = 1;
setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, &y, sizeof(y));
while (writes != buff.size()) {
int64_t out = write(sock, buff.c_str() + writes, buff.size() - writes);
if (out < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) continue;
throw std::runtime_error("Failed to write to socket");
}
writes += out;
}
}
// _____________________________________________________________________________
void HttpServer::handle() {
int connection = -1;
while ((connection = _jobs.get()) != -1) {
Answer answ;
try {
Req req = getReq(connection);
answ = _handler->handle(req, connection);
answ.gzip = gzipSupport(req);
} catch (HttpErr err) {
answ = Answer{err.what(), err.what(), false, {}};
} catch (...) {
// catch everything to make sure the server continues running
answ = Answer{
"500 Internal Server Error", "500 Internal Server Error", false, {}};
}
send(connection, &answ);
close(connection);
}
}
// _____________________________________________________________________________
bool HttpServer::gzipSupport(const Req& req) {
bool accepts = false;
// decide according to
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
for (const auto& kv : req.params) {
if (kv.first == "Accept-Encoding") {
for (const auto& encoding : split(kv.second, ',')) {
std::vector<std::string> parts = split(encoding, ';');
for (size_t i = 0; i < parts.size(); i++) {
parts[i] = trim(parts[i]);
}
if (parts[0] == "*" && ((parts.size() == 1) || parts[1] != "q=0"))
accepts = true;
if (parts[0] == "gzip") accepts = true;
if (parts.size() > 1 && parts[1] == "q=0") accepts = false;
}
}
}
return accepts;
}
// _____________________________________________________________________________
Req HttpServer::getReq(int connection) {
char buf[BSIZE + 1];
size_t rcvd = 0;
int64_t curRcvd = 0;
HeaderState state = NONE;
Req ret{"", "", "", "", {}};
char *tmp, *tmp2;
char* brk = 0;
while ((curRcvd = read(connection, buf + rcvd, BSIZE - rcvd))) {
if (curRcvd < 0) {
if (errno == EAGAIN || errno == EINTR) continue;
throw HttpErr("500 Internal Server Error");
}
// parse request
for (int i = 0; i < curRcvd; i++) {
if (brk) break;
char* c = buf + rcvd + i;
switch (state) {
case NONE:
state = I_COM;
tmp = c;
continue;
case I_VER:
if (*c == '\n') {
*c = 0;
ret.ver = trim(tmp);
state = A_KEY;
}
continue;
case I_URL:
if (*c == ' ') {
*c = 0, ret.url = trim(tmp);
tmp = c + 1;
state = I_VER;
} else if (*c == '\n') {
*c = 0, ret.url = trim(tmp);
state = A_KEY;
}
continue;
case I_COM:
if (*c == ' ') {
*c = 0, ret.cmd = trim(tmp);
tmp = c + 1;
state = I_URL;
} else if (*c == '\n') {
*c = 0, ret.cmd = trim(tmp);
state = A_KEY;
}
continue;
case A_KEY:
if (*c == '\r') *c = ' ';
if (*c == '\n')
brk = c + 1;
else if (*c != ' ') {
state = I_KEY;
tmp = c;
}
continue;
case I_KEY:
if (*c == ':') {
*c = 0;
state = A_VAL;
}
continue;
case A_VAL:
if (*c != ' ') {
state = I_VAL;
tmp2 = c;
}
continue;
case I_VAL:
if (*c == '\r') *c = ' ';
if (*c == '\n') {
*c = 0;
ret.params[tmp] = trim(tmp2);
state = A_KEY;
}
continue;
}
}
rcvd += curRcvd;
// buffer is full
if (rcvd == BSIZE) throw HttpErr("431 Request Header Fields Too Large");
if (brk) break;
}
// POST payload
if (ret.cmd == "POST") {
size_t size = 0;
if (ret.params.count("Content-Length"))
size = atoi(ret.params["Content-Length"].c_str());
if (size) {
char* postBuf = new char[size + 1];
postBuf[size] = 0;
size_t rem = 0;
// copy existing to new buffer
if ((int)rcvd > brk - buf) {
rem = std::min(size, rcvd - (brk - buf));
memcpy(postBuf, brk, rem);
}
rcvd = 0;
if (rem < size) {
while ((curRcvd = read(connection, postBuf + rcvd + rem, size - rem))) {
if (curRcvd == -1 && (errno == EAGAIN || errno == EINTR)) continue;
if (curRcvd == -1) {
postBuf[rcvd + 1] = 0;
break;
}
rcvd += curRcvd;
if (rcvd == size - rem) break;
}
}
ret.payload = postBuf;
delete[] postBuf;
}
}
return ret;
}
// _____________________________________________________________________________
std::string HttpServer::compress(const std::string& str, std::string* enc) {
#ifdef ZLIB_FOUND
// do not compress small payloads
if (str.size() < 500) return str;
std::string ret;
// based on http://www.zlib.net/zlib_how.html
z_stream defStr;
defStr.zalloc = Z_NULL;
defStr.zfree = Z_NULL;
defStr.opaque = Z_NULL;
defStr.avail_in = 0;
defStr.next_in = Z_NULL;
// fail silently with no compression at all
if (deflateInit2(&defStr, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8,
Z_DEFAULT_STRATEGY) != Z_OK)
return str;
defStr.next_in = reinterpret_cast<z_const Bytef*>(str.c_str());
defStr.avail_in = static_cast<unsigned int>(str.size());
size_t cSize = 0;
do {
if (ret.size() < (cSize + BSIZE_C)) ret.resize(cSize + BSIZE_C);
defStr.avail_out = BSIZE_C;
defStr.next_out = reinterpret_cast<Bytef*>(&ret[0] + cSize);
deflate(&defStr, Z_FINISH);
cSize += BSIZE_C - defStr.avail_out;
} while (defStr.avail_out == 0);
deflateEnd(&defStr);
ret.resize(cSize);
if (ret.size() > str.size()) return str;
*enc = "gzip";
return ret;
#else
return str;
#endif
}
// _____________________________________________________________________________
void HttpServer::run() {
Socket socket(_port);
std::vector<std::thread> thrds(_threads);
for (auto& thr : thrds) thr = std::thread(&HttpServer::handle, this);
while (1) _jobs.add(socket.wait());
}
// _____________________________________________________________________________
void Queue::add(int c) {
if (c < 0) return;
{
std::unique_lock<std::mutex> lock(_mut);
_jobs.push(c);
}
_hasNew.notify_one();
}
// _____________________________________________________________________________
int Queue::get() {
std::unique_lock<std::mutex> lock(_mut);
while (_jobs.empty()) _hasNew.wait(lock);
int next = _jobs.front();
_jobs.pop();
return next;
}

134
src/util/http/Server.h Normal file
View file

@ -0,0 +1,134 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <map>
#include <mutex>
#include <queue>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#ifndef UTIL_HTTP_SERVER_H_
#define UTIL_HTTP_SERVER_H_
namespace util {
namespace http {
// socket backlog size
const static size_t BLOG = 128;
// socket read buffer size
const static size_t BSIZE = 4 * 1024;
// zlib compression buffer size
const size_t BSIZE_C = 128 * 1024;
// states for HTTP header parser
enum HeaderState { NONE, I_COM, I_URL, I_VER, A_KEY, I_KEY, A_VAL, I_VAL };
/*
* HTTP Error
*/
class HttpErr : public std::exception {
public:
HttpErr(std::string msg) : _msg(msg) {}
~HttpErr() throw() {}
virtual const char* what() const throw() { return _msg.c_str(); }
private:
std::string _msg;
uint16_t _code;
};
/*
* HTTP Request
*/
struct Req {
std::string cmd, url, ver, payload;
std::unordered_map<std::string, std::string> params;
};
/*
* HTTP Answer
*/
struct Answer {
std::string status, pl;
bool gzip;
std::unordered_map<std::string, std::string> params;
};
/*
* Virtual handler provider class
*/
class Handler {
public:
virtual Answer handle(const Req& request, int connection) const = 0;
};
/*
* Queue of connections to handle
*/
class Queue {
public:
void add(int c);
int get();
private:
std::mutex _mut;
std::queue<int> _jobs;
std::condition_variable _hasNew;
};
/*
* Socket wrapper
*/
class Socket {
public:
Socket(int port);
~Socket();
int wait();
private:
int _sock;
};
/*
* Simple HTTP server, must provide a pointer to a class instance implementing
* virtual class Handler.
*/
class HttpServer {
public:
HttpServer(int port, const Handler* h) : HttpServer(port, h, 0) {}
HttpServer(int port, const Handler* h, size_t threads)
: _port(port), _handler(h), _threads(threads) {
if (!_threads) _threads = 8 * std::thread::hardware_concurrency();
}
void run();
private:
int _port;
Queue _jobs;
const Handler* _handler;
size_t _threads;
void handle();
static void send(int sock, Answer* aw);
static Req getReq(int connection);
static std::string compress(const std::string& str, std::string* enc);
static bool gzipSupport(const Req& req);
};
} // http
} // util
#endif // UTIL_HTTP_SERVER_H_

View file

@ -8,6 +8,7 @@
#include "util/String.h" #include "util/String.h"
#include "util/geo/Geo.h" #include "util/geo/Geo.h"
#include "util/json/Writer.h" #include "util/json/Writer.h"
#include "util/graph/Algorithm.h"
#include "util/graph/DirGraph.h" #include "util/graph/DirGraph.h"
#include "util/graph/UndirGraph.h" #include "util/graph/UndirGraph.h"
#include "util/graph/Dijkstra.h" #include "util/graph/Dijkstra.h"
@ -403,6 +404,46 @@ CASE("editdist") {
EXPECT(util::editDist("hello", "hello") == (size_t)0); EXPECT(util::editDist("hello", "hello") == (size_t)0);
}}, }},
// ___________________________________________________________________________
{
CASE("prefixeditdist") {
EXPECT(util::prefixEditDist("hello", "hello", 0) == (size_t)0);
EXPECT(util::prefixEditDist("hello", "hello", 100) == (size_t)0);
EXPECT(util::prefixEditDist("hello", "hello") == (size_t)0);
EXPECT(util::prefixEditDist("hel", "hello") == (size_t)0);
EXPECT(util::prefixEditDist("hel", "hello", 0) == (size_t)0);
EXPECT(util::prefixEditDist("hel", "hello", 1) == (size_t)0);
EXPECT(util::prefixEditDist("hel", "hello", 2) == (size_t)0);
EXPECT(util::prefixEditDist("hal", "hello", 2) == (size_t)1);
EXPECT(util::prefixEditDist("hal", "hello", 1) == (size_t)1);
EXPECT(util::prefixEditDist("hal", "hello", 0) > (size_t)0);
EXPECT(util::prefixEditDist("fel", "hello", 0) > (size_t)0);
EXPECT(util::prefixEditDist("fel", "hello", 1) == (size_t)1);
EXPECT(util::prefixEditDist("fel", "hello", 2) == (size_t)1);
EXPECT(util::prefixEditDist("fal", "hello", 2) == (size_t)2);
EXPECT(util::prefixEditDist("fal", "hello", 1) > (size_t)1);
EXPECT(util::prefixEditDist("fal", "hello", 0) > (size_t)0);
EXPECT(util::prefixEditDist("far", "hello", 0) > (size_t)0);
EXPECT(util::prefixEditDist("far", "hello", 1) > (size_t)1);
EXPECT(util::prefixEditDist("far", "hello", 2) > (size_t)2);
EXPECT(util::prefixEditDist("far", "hello", 3) == (size_t)3);
EXPECT(util::prefixEditDist("far", "hello", 4) == (size_t)3);
EXPECT(util::prefixEditDist("far", "hello") == (size_t)3);
EXPECT(util::prefixEditDist("hefar", "hello") == (size_t)3);
EXPECT(util::prefixEditDist("hefaree", "hello") == (size_t)5);
EXPECT(util::prefixEditDist("helloo", "hello") == (size_t)1);
EXPECT(util::prefixEditDist("helloo", "hello", 0) > (size_t)0);
EXPECT(util::prefixEditDist("helloo", "hello", 1) == (size_t)1);
EXPECT(util::prefixEditDist("helloo", "hello", 2) == (size_t)1);
EXPECT(util::prefixEditDist("", "hello", 2) == (size_t)0);
EXPECT(util::prefixEditDist("e", "hello", 2) == (size_t)1);
EXPECT(util::prefixEditDist("el", "hello", 2) == (size_t)1);
EXPECT(util::prefixEditDist("ello", "hello", 2) == (size_t)1);
EXPECT(util::prefixEditDist("hell", "hello", 2) == (size_t)0);
EXPECT(util::prefixEditDist("hell", "", 2) > (size_t)2);
EXPECT(util::prefixEditDist("hell", "") == (size_t)4);
}},
// ___________________________________________________________________________ // ___________________________________________________________________________
{ {
CASE("toString") { CASE("toString") {
@ -447,6 +488,51 @@ CASE("replace") {
EXPECT(b == "loree aaaau aaaau loree"); EXPECT(b == "loree aaaau aaaau loree");
}}, }},
// ___________________________________________________________________________
{
CASE("Connected components undirected") {
UndirGraph<std::string, int> g;
auto a = g.addNd("A");
auto b = g.addNd("B");
auto c = g.addNd("C");
auto d = g.addNd("D");
auto e = g.addNd("E");
g.addEdg(a, c, 1);
g.addEdg(a, b, 5);
g.addEdg(d, c, 1);
g.addEdg(d, b, 3);
g.addEdg(e, d, 1);
g.addEdg(e, b, 1);
auto comps = util::graph::Algorithm::connectedComponents(g);
EXPECT(comps.size() == static_cast<size_t>(1));
EXPECT(comps[0].count(a));
EXPECT(comps[0].count(b));
EXPECT(comps[0].count(c));
EXPECT(comps[0].count(d));
EXPECT(comps[0].count(e));
auto f = g.addNd("F");
comps = util::graph::Algorithm::connectedComponents(g);
EXPECT(comps.size() == static_cast<size_t>(2));
auto gn = g.addNd("G");
comps = util::graph::Algorithm::connectedComponents(g);
EXPECT(comps.size() == static_cast<size_t>(3));
g.addEdg(f, gn, 1);
comps = util::graph::Algorithm::connectedComponents(g);
EXPECT(comps.size() == static_cast<size_t>(2));
g.addEdg(f, a, 1);
comps = util::graph::Algorithm::connectedComponents(g);
EXPECT(comps.size() == static_cast<size_t>(1));
}},
// ___________________________________________________________________________ // ___________________________________________________________________________
{ {
CASE("Edge-based Dijkstra directed, 1 to all") { CASE("Edge-based Dijkstra directed, 1 to all") {

@ -1 +1 @@
Subproject commit 00e19bfbdc300eb064fbceef2efaed2ccedfda88 Subproject commit 5081d32879c30456f6cb515342a3096c5a0d7de6