Compare commits

...

171 commits

Author SHA1 Message Date
Charles P.
e2f9679c22
🚧 rollback to v3 of actions/upload-artifact
All checks were successful
GTFS Shapes Generation / modify_routes (push) Successful in 58s
waiting for https://codeberg.org/forgejo/forgejo/issues/3485 to be fixed
2024-06-01 22:41:01 +02:00
Charles P.
172f7eafdb
🚧 fix OSM data format in test.yml workflow
Some checks failed
GTFS Shapes Generation / modify_routes (push) Failing after 49s
2024-06-01 22:34:36 +02:00
Charles P.
eb2a556276
🚧 add test.yml
Some checks failed
GTFS Shapes Generation / modify_routes (push) Failing after 2m41s
2024-06-01 22:26:36 +02:00
Charles P.
0a2f4eecda
🚧 add test.yml
Some checks failed
GTFS Shapes Generation / modify_routes (push) Failing after 2s
2024-06-01 22:25:21 +02:00
Charles P.
e779970387
🚧 make pfaedle an action 2024-06-01 20:11:38 +02:00
Charles P.
ea8185a784
🚧 remove github actions 2024-06-01 20:07:40 +02:00
Paul
33508404e1
Update minimum cmake version to 3.5 (#54)
Signed-off-by: paulober <44974737+paulober@users.noreply.github.com>
2024-04-05 18:41:44 +02:00
Patrick Brosi
e503fba5b6
Update README.md 2024-03-04 13:47:13 +01:00
Patrick Brosi
6a1ae5fc77
Update README.md 2024-03-04 13:45:28 +01:00
Patrick Brosi
e643166555
typo 2024-03-04 13:45:05 +01:00
Patrick Brosi
7ccc303022 write attributions.txt crediting OSM contributors, mention OSM copyright in README, closes #53 2024-03-04 13:41:19 +01:00
Patrick Brosi
0037a83f68 dont install OMP in docker - not needed anymore 2024-02-15 13:41:41 +01:00
Patrick Brosi
67b9f7f6d2 remove TODOs 2024-02-15 12:53:33 +01:00
Patrick Brosi
eda6f50fbb
Update README.md 2024-02-15 09:08:36 +01:00
Patrick Brosi
5ced5e5a50 mount gtfs-out in Docker example, closes #45 2024-02-15 09:07:17 +01:00
Patrick Brosi
76c1324353
Consistent use of $ in README 2024-02-15 09:01:17 +01:00
Patrick Brosi
1752da6147 update .dockerignore 2024-02-15 08:57:47 +01:00
Patrick Brosi
a6b09de921 fix Docker file (build *and* run on bookworm), install libzip, zlib and libbz2, delete obsolete push-docker-image.sh, update README 2024-02-15 08:53:23 +01:00
Leonard Ehrenfried
9a5fc2abdf
Automatically publish container image to Github Container Repo (#49)
* Update Debian base image to newest version

* Add workflow for building container image

* Use date-based tags
2024-02-15 08:23:13 +01:00
Patrick Brosi
ad0252695f add -W [ --warn ] flag to output warning messages, update cppgtfs 2024-01-30 12:51:22 +01:00
Patrick Brosi
a078b22e18 Revert "enable flto link time optimization"
This reverts commit 81a8d9d65b.
2024-01-25 14:19:51 +01:00
Patrick Brosi
81a8d9d65b enable flto link time optimization 2024-01-25 14:08:53 +01:00
Patrick Brosi
d87eacb6e0 catch more ferry terminals explicitly 2024-01-24 14:51:10 +01:00
Patrick Brosi
22bc04aa9d dont delete orphan edges anymore, might remove some candidates for edge snapping 2024-01-24 14:06:54 +01:00
Patrick Brosi
136fae1cf1 update cppgtfs 2024-01-21 16:29:45 +01:00
Patrick Brosi
3b35ef638d update cppgtfs 2024-01-20 18:41:30 +01:00
Patrick Brosi
80504b21fe use int instead of char to hold return value of getopt_long to avoid issues if the default char is unsigned (arm64), update util and cppgtfs to fix further issues on such architectures 2024-01-02 16:26:07 +01:00
Patrick Brosi
6bbb461b02 update cppgtfs 2023-11-22 11:04:21 +01:00
Patrick Brosi
83851eed49 update util 2023-11-22 10:36:14 +01:00
Patrick Brosi
6adfc5feff update cppgtfs 2023-11-21 17:33:43 +01:00
Patrick Brosi
de9583877c update cppgtfs 2023-11-07 01:14:05 +01:00
Patrick Brosi
be3c41b784 include stdint.h in Osm.h 2023-11-07 01:02:25 +01:00
Patrick Brosi
9d00fa2d75 update util 2023-11-07 00:58:53 +01:00
Patrick Brosi
62e2ea36e1 update util 2023-11-07 00:47:55 +01:00
Patrick Brosi
dd05506c42 update cppgtfs, add (hidden as of now, as the mode is not complete) option -F to keep additional non-standard GTFS fields 2023-11-06 17:59:29 +01:00
Patrick Brosi
dfb62babd9 update util 2023-10-17 14:55:25 +02:00
Patrick Brosi
096d6800df bump required cmake to 3.1, use CMAKE_CXX_STANDARD to require C++-11 2023-10-17 14:42:28 +02:00
Patrick Brosi
363c52252d rm .travis.yml 2023-10-16 13:29:42 +02:00
Patrick Brosi
19350108ce update util 2023-10-16 12:57:44 +02:00
Patrick Brosi
3e350185f8 run apt update in workflow before installing packages 2023-10-16 12:43:10 +02:00
Patrick Brosi
500a257ca3 update util and xml 2023-10-16 12:17:36 +02:00
Patrick Brosi
aa674f81ae update pfxml 2023-10-16 09:58:39 +02:00
Patrick Brosi
f6228ee7a2 better handling of trip hops with an impossibly small travel time, minor homogenization of edge case behavior of hops() and hopsFast() for hops near maxCost 2023-10-13 22:51:23 +02:00
Patrick Brosi
cb606ef75f correctly handle output zip files in directories 2023-10-13 12:43:59 +02:00
Patrick Brosi
7a772486d0 update subprojects, refactoring 2023-10-12 13:03:21 +02:00
Patrick Brosi
1c459816de remove stray resize of maxCosts which seems to have been (wrongly) used to init all values of maxCosts to 0 (they are already zero via construction in line 33). Probably a refactoring gone wrong. Fixes #48. 2023-10-11 12:42:02 +02:00
Patrick Brosi
c27f8a852f remove cpplint 2023-10-10 13:18:14 +02:00
Patrick Brosi
1b7ade99be bump minimum required cmake version, dont hardcode build path to build in CMakeLists.txt, update pfxml 2023-10-10 13:10:34 +02:00
Patrick Brosi
1d8ce1aa7c use util submodule 2023-10-06 12:39:49 +02:00
Patrick Brosi
3f43538010 update util 2023-10-06 12:17:20 +02:00
Patrick Brosi
580f1807f3 update util 2023-10-06 12:14:03 +02:00
Patrick Brosi
a8c23b2c02 update util 2023-10-04 17:24:55 +02:00
Patrick Brosi
56ab60c3fd update pfxml 2023-09-06 22:46:28 +02:00
Patrick Brosi
194f37fcd5 fixed precision for runtime output 2023-09-06 13:02:06 +02:00
Patrick Brosi
57ee83644e check if libzip and bzip2 have been found before adding them to include_directories 2023-09-06 12:43:33 +02:00
Patrick Brosi
5c1ad4579f Compute costs based on collapsed edge geometry length, not on distance between adjacent nodes, fixing a regression introduced last year. Fixes #47 2023-09-06 12:13:46 +02:00
Patrick Brosi
646707628f update readme 2023-08-24 17:35:22 +02:00
Patrick Brosi
4f14dad215 update example 2023-08-24 17:34:02 +02:00
Patrick Brosi
58c47bb1a5 bz2 support for OSM reading and writing 2023-08-24 17:29:01 +02:00
Patrick Brosi
3d6ed3e2fd update readme 2023-08-23 13:53:48 +02:00
Patrick Brosi
d43d9fc908 gz support for OSM xml reading and writing 2023-08-23 13:46:23 +02:00
Patrick Brosi
ed15d5d1de update cppgtfs 2023-08-22 12:53:12 +02:00
Patrick Brosi
25f6fb38b2 update cppgtfs 2023-08-22 12:41:26 +02:00
Patrick Brosi
33a92077df correctly handle trips with no stop times, fixes #46 2023-08-21 13:56:46 +02:00
Patrick Brosi
45f508bf34 update README 2023-04-04 13:49:29 +02:00
Patrick Brosi
c0c2da57e7 remote ubuntu 18.04 from workflows 2023-04-03 21:09:52 +02:00
Patrick Brosi
43904f2346 use new cppgtfs version, adjust motToStr methods to handle sets of MOTs 2023-04-03 20:57:25 +02:00
Patrick Brosi
16a8f3fcad update cppgtfs 2022-12-13 15:50:15 +01:00
Patrick Brosi
bb21a9651d fix typo in libutil 2022-12-13 15:35:41 +01:00
Patrick Brosi
e085cfb1b2 remove superfluous space 2022-11-03 14:27:21 +01:00
Patrick Brosi
59d4f96c68 if no GTFS feed is supplied, use the XML input bounding box (this allows XML filtering without GTFS feeds), write <osm> tag with "version" and "generator" attributes. Fixes #26 2022-11-03 13:27:54 +01:00
Patrick Brosi
3e6e7ebfed update cppgtfs to include new route types trolleybus and monorail 2022-11-03 12:37:24 +01:00
Patrick Brosi
167753142c update cppgtfs 2022-10-12 13:06:35 +02:00
Patrick Brosi
496ee739bc update cppgtfs, also link shapevl against libzip 2022-10-12 12:48:22 +02:00
Patrick Brosi
29f5b9bd10 add libzip include dirs 2022-10-12 12:40:56 +02:00
Patrick Brosi
6f6262f9b1 adjust ShapevlMain.cpp to new parser structure 2022-10-12 12:30:22 +02:00
Patrick Brosi
79dd70c2f5 set LIBZIP_LIBRARY to "" if libzip was not found 2022-10-12 12:22:16 +02:00
Patrick Brosi
f71207159c dont fail in FindLibZip if if libzip cannot be found 2022-10-12 12:11:58 +02:00
Patrick Brosi
57545ced30 make libzip optional 2022-10-12 12:08:52 +02:00
Patrick Brosi
0699fccb1e update cppgtfs, support pathways, parse continuous_pickup and drop_off 2022-10-12 00:24:01 +02:00
Patrick Brosi
c68cde67dc add findlibzip.cmake 2022-10-11 17:53:56 +02:00
Patrick Brosi
9a8e5d4e31 update cppgtfs, support ZIP reading and writing for GTFS feeds 2022-10-11 15:04:24 +02:00
Patrick Brosi
b4b08baeca update cppgtfs 2022-10-04 14:35:21 +02:00
Patrick Brosi
0209c6fa04 update util lib 2022-09-12 16:19:22 +02:00
Patrick Brosi
760ee20c35 typo 2022-08-30 00:40:41 +02:00
Patrick Brosi
83a073ab0b add badge 2022-08-29 15:25:29 +02:00
Patrick Brosi
d059870f8c update util 2022-08-29 15:05:33 +02:00
Patrick Brosi
d60c291402 print failing tests 2022-08-29 14:38:23 +02:00
Patrick Brosi
c5ebfbb57b include clocale in PfaedleMain.cpp 2022-08-29 14:26:03 +02:00
Patrick Brosi
e8b3b33d3c update util lib 2022-08-29 14:22:42 +02:00
Patrick Brosi
8fb63b80bd add github action for build 2022-08-29 14:20:31 +02:00
Tirreg
04de10a3ae
fixed small inconsistency in README (#34) 2022-04-19 10:42:34 +02:00
Patrick Brosi
8f6be38f8c
update readme 2022-04-11 12:38:22 +02:00
Patrick Brosi
6f7b4a5694 update readme 2022-03-28 15:20:14 +02:00
Patrick Brosi
f867e2bb08 lower required clang version 2022-03-28 15:02:20 +02:00
Patrick Brosi
361063185e update util lib 2022-03-28 14:59:55 +02:00
Patrick Brosi
a58ed3d54d adjust defaults 2022-03-09 17:11:09 +01:00
Patrick Brosi
e15b90af9f comment 2022-02-07 09:35:28 +01:00
Patrick Brosi
13b6436a41 adjust defaults 2022-02-01 12:43:46 +01:00
Patrick Brosi
af4d2293a6 typo 2022-02-01 09:20:17 +01:00
Patrick Brosi
db437e1d95 change some default values 2022-02-01 01:59:42 +01:00
Patrick Brosi
760998144f increase routing_station_move_penalty_fac to 0.0039 based on evaluation runs 2022-01-31 23:57:27 +01:00
Patrick Brosi
647a596dea make jaccard-geodist the default statsimi classifier 2022-01-31 23:44:53 +01:00
Patrick Brosi
cdbfb0966d correctly read osm_max_osm_station_distance, use default of 10 meters 2022-01-22 23:01:00 +01:00
Patrick Brosi
e402f19cdb output an5 2022-01-22 13:36:37 +01:00
Patrick Brosi
396ea175e7 shorter output 2022-01-22 02:24:24 +01:00
Patrick Brosi
54409ec24d refactoring 2022-01-22 02:14:25 +01:00
Patrick Brosi
ff8748ef3f some bugfixes 2022-01-22 00:57:44 +01:00
Patrick Brosi
15e84d930a update segmentation in shapevl, add --unique mode 2022-01-21 23:59:14 +01:00
Patrick Brosi
c3bc12eb8c bugfixes 2022-01-17 15:11:24 +01:00
Patrick Brosi
46482788ff remove debug output 2022-01-17 13:50:18 +01:00
Patrick Brosi
39fb167cbf some minor changes 2022-01-17 13:29:35 +01:00
Patrick Brosi
ea79e3ac52 remove gtfs_has_shapes from stats output, was always true as checked after the shapes were calculated 2022-01-14 17:44:48 +01:00
Patrick Brosi
516f04b9c5 bts classifier 2022-01-13 21:41:52 +01:00
Patrick Brosi
749044ce97 fix misnamed turning_circle parameter 2022-01-12 20:21:27 +01:00
Patrick Brosi
1e516f4d2b faster shapevl 2022-01-12 17:20:10 +01:00
Patrick Brosi
1febc192ce add "all" to keep-tags option for osmfilter 2022-01-11 23:21:12 +01:00
Patrick Brosi
c25d174e60 add --osmfilter option 2022-01-11 17:32:50 +01:00
Patrick Brosi
6473dcdb52 json output mode for shapevl 2022-01-09 23:58:18 +01:00
Patrick Brosi
f10397db41 add gaussian noise parameter 2022-01-09 15:09:41 +01:00
Patrick Brosi
0e5213f049 fix 2022-01-07 17:41:03 +01:00
Patrick Brosi
6b445b84d1 some updates to shapevl 2022-01-07 17:35:30 +01:00
Patrick Brosi
3a08b63d8e better stats.json 2022-01-05 11:24:42 +01:00
Patrick Brosi
4c29892658 * speed up hop-to-hop calculations
* better and faster trip clustering: trip tries
* add --write-colors to extract line colors from OSM data
* refactor config parameter names, update default pfaedle.cfg
* add --stats for writing a stats.json file
* add --no-fast-hops, --no-a-star, --no-trie for debugging
* general refactoring
2022-01-04 17:19:27 +01:00
Patrick Brosi
f1822868c5 include cstddef in Node.h 2021-11-30 12:46:24 +01:00
Patrick Brosi
57de18844f update cppgtfs 2021-11-30 12:45:58 +01:00
Patrick Brosi
59e527bcb4 update repo URL 2021-11-09 12:01:49 +01:00
Patrick Brosi
364d6a19e5 update cppgtfs 2021-09-10 12:53:28 +02:00
Patrick Brosi
cfc41d01e7 remove check which was alywas true 2021-09-10 12:36:19 +02:00
Patrick Brosi
16e66e59a5 update cppgtfs 2021-09-09 00:14:52 +02:00
Patrick Brosi
366e4b2aba update cppgtfs 2021-09-08 21:21:44 +02:00
Patrick Brosi
deaaf23caf linter 2021-07-24 11:02:02 +02:00
Patrick Brosi
cea4474631 write doubles as bytes to shape container, not strings, to safe some disk space and speed up writing 2021-07-24 11:02:02 +02:00
graue70
e0e4ee1f2b
Fix incorrect URL in README (#30)
Old URL forced login.
2021-06-27 12:20:15 +02:00
Patrick Brosi
087ea0310b move os condition to condition clause 2020-12-03 14:44:14 +01:00
Patrick Brosi
73101fcc03 only build image on linux 2020-12-03 12:50:17 +01:00
Patrick Brosi
dd64e075c8 only deploy on linux using gcc 2020-12-03 01:38:08 +01:00
Patrick Brosi
8fe7a30e31 add docker service to travis.yml 2020-12-03 00:05:34 +01:00
Patrick Brosi
2f7e6a028f update readme 2020-12-02 19:29:30 +01:00
Patrick Brosi
5d2bb9bbfd Merge branch 'derhuerst-docker-image' 2020-12-02 19:27:17 +01:00
Patrick Brosi
b9897bdd97 add docker pw and uname 2020-12-02 19:26:40 +01:00
Jannis R
9a8a64d38e
readme: explain docker usage 📝 2020-12-02 17:53:31 +01:00
Jannis R
4aa500b867
Travis CI: build & publish Docker image 💚 2020-12-02 13:53:50 +01:00
Jannis R
2a01eda564
add Dockerfile & .dockerignore 2020-12-02 13:53:29 +01:00
Patrick Brosi
b0a2cff43a fix possible race condition in Normalizer cache 2020-11-20 17:00:20 +01:00
Alexander Held
08b0685ad1
Optional transit line penalties when no explicit line was given (#19)
* Enable transit line penalties also when no explicit line was given

Currently, if no transit line is given, all edges get penalty factor 0
regardless of whether there are transit lines following the edge or not.
The change introduced in this commit will give penalty factor 0 if there
is at least one transit line following the edge and penalty factor 1 if
there are no tranist lines at all following the edge.
Behavior in the case when a transit line is given is untouched.

This should improve routing results by preferring edges with
associated transit lines. This used to be the behaviour before
some refactoring. Maybe it was broken unintentionally.
2020-10-13 20:27:20 +02:00
Patrick Brosi
e06c15656b
Merge pull request #20 from aheld84/edge-splitting
Fix edge splitting fragment ending up with wrong geometry in edge grid
2020-09-30 17:57:14 +02:00
Alexander Held | geOps
c023bb963d Fix edge splitting fragment being registered with wrong geometry in edge grid
If I'm not mistaken, there is a typo in the OsmBuilder when an edge is split
into two edges to insert a station: one of the new edges is registered with
the wrong geometry in the edge grid. This should fix it.
2020-09-30 16:20:04 +02:00
Patrick Brosi
69dc466605 remove empty variable from link 2020-05-26 10:23:24 +02:00
Patrick Brosi
9c51f2799d
Merge pull request #13 from rurseekatze/station-id-normalize
Fix name of config param
2020-02-26 16:50:56 +01:00
Alexander Matheisen
6c42f8244e Fix name of config param 2020-02-26 11:36:34 +01:00
Patrick Brosi
1163f11e57
Merge pull request #12 from Benitoite/patch-1
CMakeLists.txt: download submodules if needed
2020-02-11 10:18:51 +01:00
Benitoite
3eb5c881fe
Detect .git instead of CMakeLists.txt in submods 2020-02-10 07:10:46 -08:00
Benitoite
6d2671900b
also check for xml submodule 2020-02-09 04:52:18 -08:00
Benitoite
f856e9865c CMakeLists.txt: download submodules if needed
Download git submodules if the CMakeLists.txt files for the configparser or cppgtfs directories are not present.
2020-02-09 04:47:13 -08:00
Patrick Brosi
9262b3ad45 update xml submodule 2019-09-16 00:42:07 +02:00
Patrick Brosi
31f9cb72ff refactoring 2019-09-16 00:39:17 +02:00
Patrick Brosi
a5931eb9ec update util 2019-09-06 15:19:22 +02:00
Patrick Brosi
06aefa538a upate util 2019-09-06 01:24:24 +02:00
Patrick Brosi
feacae92ef output all GeoJSON as WGS84 to be RFC 7946 conform 2019-07-23 22:47:35 +02:00
Patrick Brosi
27da2a9c9e output -T geojson as WGS84 coordinates 2019-07-23 21:32:07 +02:00
Patrick Brosi
bfca604ab5 explicit permissions for install targets 2019-06-28 15:10:31 +02:00
Patrick Brosi
79303f12f2 update /util 2019-06-28 15:10:15 +02:00
Patrick Brosi
df1ba83a53 check if tmp is null 2019-05-14 00:46:53 +02:00
Patrick Brosi
347badf770 better tmp dir selection 2019-05-14 00:11:15 +02:00
Patrick Brosi
9d19e344d3 refactoring 2019-05-13 20:05:51 +02:00
Patrick Brosi
67308d02e9 centralize code for tmp storage file creation 2019-05-13 18:56:43 +02:00
Patrick Brosi
ee948a8810 prevent possible race condition while obtaining collision-free shape ID 2019-05-13 15:05:34 +02:00
Patrick Brosi
0db43ad457 update cfgparser 2019-05-02 01:20:35 +02:00
Patrick Brosi
78f9eb7044 update cppgtfs 2019-05-01 22:47:00 +02:00
Patrick Brosi
4cf1cc2b39
Merge pull request #4 from ad-freiburg/niklas88-python-2
Explicitly use python 2 in shebang, fixes #3
2019-04-08 12:45:01 +02:00
141 changed files with 7113 additions and 25285 deletions

4
.dockerignore Normal file
View file

@ -0,0 +1,4 @@
/build
/Dockerfile
gtfs-out

View file

@ -0,0 +1,33 @@
name: GTFS Shapes Generation
on: [push]
jobs:
modify_routes:
runs-on: docker
steps:
- name: Download sample feed
run: curl -o sample-feed.zip https://download.data.public.lu/resources/horaires-et-arrets-des-transport-publics-gtfs/20240530-080402/gtfs-20240529-20240621.zip
- name: Download OSM data
run: curl -o luxembourg-latest.osm.pbf https://download.geofabrik.de/europe/luxembourg-latest.osm.pbf
- name: Convert OSM data to .osm
run: |
apt update && \
apt install -y osmctools && \
osmconvert luxembourg-latest.osm.pbf -o=luxembourg-latest.osm
- name: Run Shape Generation
uses: gtfs-actions/generate-shapes@main
with:
gtfs_file: sample-feed.zip
osm_file: luxembourg-latest.osm
mot: bus
output_file: modified-feed.zip
- name: Upload modified feed
uses: actions/upload-artifact@v3
with:
name: modified-feed
path: modified-feed.zip

7
.gitmodules vendored
View file

@ -1,9 +1,12 @@
[submodule "src/cppgtfs"]
path = src/cppgtfs
url = https://ad-git.informatik.uni-freiburg.de/ad/cppgtfs.git
url = https://github.com/ad-freiburg/cppgtfs.git
[submodule "src/xml"]
path = src/xml
url = https://git.patrickbrosi.de/patrick/xmlparser
url = https://github.com/patrickbr/pfxml.git
[submodule "src/configparser"]
path = src/configparser
url = https://git.patrickbrosi.de/patrick/configparser
[submodule "src/util"]
path = src/util
url = https://github.com/ad-freiburg/util

View file

@ -1,33 +0,0 @@
language: cpp
os:
- linux
- osx
compiler:
- gcc
- clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- cmake
before_install:
- export LD_LIBRARY_PATH=$(if [[ $CXX == "clang++" ]]; then echo -n '/usr/local/clang/lib'; fi)
before_script:
- mkdir build
- cd build
- cmake ..
script:
- make -j4
- ctest --verbose
notifications:
email:
on_success: never
on_failure: always

View file

@ -1,37 +1,23 @@
cmake_minimum_required (VERSION 2.8)
cmake_minimum_required (VERSION 3.5)
set(CMAKE_CXX_STANDARD 11)
project (pfaedle)
if (CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
string(SUBSTRING ${CMAKE_BUILD_TYPE} 0 1 FIRST_CHAR)
string(TOUPPER ${FIRST_CHAR} FIRST_CHAR)
string(REGEX REPLACE "^.(.*)" "${FIRST_CHAR}\\1" CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
endif()
if (CMAKE_BUILD_TYPE STREQUAL "DEBUG")
set(CPPLINT "${CMAKE_SOURCE_DIR}/cpplint.py")
include(cmake/cpplint.cmake)
endif()
set(CPPLINT_PROJECT_ROOT "src")
enable_testing()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build")
find_package(OpenMP)
if (OPENMP_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}")
# set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake
if(OPENMP_FOUND)
set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic")
else()
message(WARNING "Configuring without OpenMP!")
set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic")
endif()
set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic -Wno-keyword-macro")
set(CMAKE_CXX_FLAGS_SANITIZE "-Og -g -fsanitize=address -fsanitize=leak -fsanitize=undefined -DLOGLEVEL=3 -DPFAEDLE_DBG=1")
set(CMAKE_CXX_FLAGS_PROFILE "-g -pg -DLOGLEVEL=3 -DPFAEDLE_DBG=1")
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DLOGLEVEL=3 -DPFAEDLE_DBG=1")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2")
@ -40,23 +26,27 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -g -DLOGLEVEL=3")
# export compile commands to tools like clang
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Compiler-specific C++11 activation.
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
if ((GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else ()
message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.8 or greater!")
endif ()
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else ()
message(FATAL_ERROR "Your C++ compiler does not support C++11.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPFAEDLE_PRECISION=${PFAEDLE_PRECISION}")
find_package(LibZip)
find_package(ZLIB)
find_package(BZip2)
if (LIBZIP_FOUND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLIBZIP_FOUND=1")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPFAEDLE_PRECISION=${PFAEDLE_PRECISION}")
if (ZLIB_FOUND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZLIB_FOUND=1")
else ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPFXML_NO_ZLIB=1")
endif()
if (BZIP2_FOUND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBZLIB_FOUND=1")
else ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPFXML_NO_BZLIB=1")
endif()
# http://brianmilco.blogspot.de/2012/11/cmake-automatically-use-git-tags-as.html
include(GetGitRevisionDescription)
@ -68,26 +58,32 @@ else()
set(VERSION_GIT_FULL "${VERSION_GIT}-${VERSION_GIT_IS_DIRTY}")
endif()
# Download submodules if needed
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/src/configparser/.git OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/cppgtfs/.git OR NOT EXISTS ${CMAKE_SOURCE_DIR}/src/xml/.git)
execute_process(
COMMAND git submodule update --init --recursive
)
endif()
add_subdirectory(src)
# tests
add_test("utilTest" utilTest)
# custom eval target
add_custom_target(
eval
COMMAND make
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval
)
add_test("pfaedleTest" pfaedleTest)
# handles install target
install(
FILES pfaedle.cfg DESTINATION etc/${PROJECT_NAME} COMPONENT config PERMISSIONS WORLD_READ
FILES pfaedle.cfg DESTINATION etc/${PROJECT_NAME} COMPONENT config PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
)
install(
FILES build/pfaedle DESTINATION bin
PERMISSIONS WORLD_EXECUTE COMPONENT binaries
FILES ${CMAKE_BINARY_DIR}/pfaedle DESTINATION bin
PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries
)
install(
FILES ${CMAKE_BINARY_DIR}/shapevl DESTINATION bin
PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries
)

25
Dockerfile Normal file
View file

@ -0,0 +1,25 @@
FROM debian:bookworm-slim AS builder
WORKDIR /app
RUN apt-get update && \
apt-get install -y g++ cmake git libzip-dev zlib1g-dev libbz2-dev
ADD . /app
RUN mkdir build && \
cd build && \
cmake .. && \
make -j && \
pwd && \
make install
FROM debian:bookworm-slim
RUN apt-get update && \
apt-get install -y libzip4 zlib1g libbz2-1.0 && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/etc/pfaedle /usr/local/etc/pfaedle
COPY --from=builder /usr/local/bin/pfaedle /usr/local/bin/pfaedle
ENTRYPOINT ["/usr/local/bin/pfaedle"]

102
README.md
View file

@ -4,44 +4,48 @@
[![Left: station-to-station path of a single bus through Stuttgart obtained from official schedule data. Right: path of the same bus map-matched by pfaedle.](geo/stuttgart_ex_res.png?raw=true)](geo/stuttgart_ex.png?raw=true)
*Left: station-to-station path of a single bus through Stuttgart obtained from official schedule data. Right: path of the same bus map-matched by pfaedle.*
[![Build
Status](https://travis-ci.org/ad-freiburg/pfaedle.svg?branch=master)](https://travis-ci.org/ad-freiburg/pfaedle)
[![Build](https://github.com/ad-freiburg/pfaedle/actions/workflows/build.yml/badge.svg)](https://github.com/ad-freiburg/pfaedle/actions/workflows/build.yml)
# pfaedle
Precise OpenStreetMap (OSM) map-matching for public transit schedules ([GTFS](https://developers.google.com/transit/gtfs/reference/) data).
Implementation and evaluation code for our paper [Sparse Map-Matching in Public Transit Networks with Turn Restrictions](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf).
First described in [this 2018 SIGSPATIAL paper](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf).
For a quick visual inspection of the shape quality, see for example the schedule data for Germany or Switzerland in our tool [TRAVIC](https://travic.app/?z=7&x=1261608.6&y=6430601.6).
## Requirements
* `cmake`
* `gcc >= 4.9` (or `clang >= 5.0`)
* `gcc >= 5.0` (or `clang >= 3.9`)
* `libzip` (*optional*, for ZIP support)
* `zlib` (*optional*, for gzip support)
* `libbz2` (*optional*, for bzip2 support)
## Building and Installation
Fetch this repository and init submodules:
```
git clone --recurse-submodules https://github.com/ad-freiburg/pfaedle
```shell
$ git clone --recurse-submodules https://github.com/ad-freiburg/pfaedle
```
```
mkdir build && cd build
cmake ..
make -j
```shell
$ mkdir build && cd build
$ cmake ..
$ make -j
```
To install, type
```
make install
```shell
$ make install
```
# General Usage
## Generating shapes for a GTFS feed
```
pfaedle -x <OSM FILE> <GTFS INPUT FOLDER>
```shell
$ pfaedle -x <OSM FILE> <GTFS INPUT FEED>
```
A shape'd version of the input GTFS feed will be written to `./gtfs-out`.
@ -49,19 +53,19 @@ A shape'd version of the input GTFS feed will be written to `./gtfs-out`.
By default, shapes are only calculated for trips that don't have a shape in the
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` flag) shapes for the GTFS dataset for Freiburg like this:
```
$ wget https://fritz.freiburg.de/csv_Downloads/VAGFR.zip && unzip VAGFR.zip
$ wget http://download.geofabrik.de/europe/germany/baden-wuerttemberg/freiburg-regbez-latest.osm.bz2 && bunzip2 freiburg-regbez-latest.osm.bz2
$ pfaedle -D -x freiburg-regbez-latest.osm .
```shell
$ wget https://fritz.freiburg.de/csv_Downloads/VAGFR.zip
$ wget http://download.geofabrik.de/europe/germany/baden-wuerttemberg/freiburg-regbez-latest.osm.bz2
$ pfaedle -D -x freiburg-regbez-latest.osm.bz2 VAGFR.zip
```
## Generating shapes for a specific MOT
To generate shapes for a specific mot only, use the `-m` option. Possible
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 route type codes (0, 1, 2, 3, 4, 5, 6, 7, or [extended route types](https://developers.google.com/transit/gtfs/reference/extended-route-types)). Integer codes will only match the specific route type, while string codes will match classes of route types. For example, `-m 101` will only match routes with `route_type` `101` (high speed rail), while `-m rail` will match any rail service encoded via a standard `route_type` `2` or an extended `route_type` describing a rail service (e.g. `100`, `101`, `102`, ...).
Multiple values can be specified (comma separated).
@ -75,6 +79,24 @@ input GTFS feed and the input configuration.
This can be used to avoid parsing (for example) the entire `planet.osm` on each
run.
## via Docker
You can use the [Docker image](https://github.com/orgs/ad-freiburg/packages/container/package/pfaedle) by mounting the OSM & GTFS data into the container:
```shell
$ docker pull ghcr.io/ad-freiburg/pfaedle:latest
$ docker run -i --rm \
# mount OSM data
--volume /path/to/osm/data:/osm \
# mount GTFS data
--volume /path/to/gtfs/data:/gtfs \
# mount default output folder gtfs-out
--volume /path/to/output-dir:/gtfs-out \
ghcr.io/ad-freiburg/pfaedle:latest \
# tell pfaedle where to find the data
-x /osm/osm-data.xml.bz2 -i /gtfs/myfeed.zip
```
## Debugging
The following flags may be useful for debugging:
@ -82,48 +104,12 @@ The following flags may be useful for debugging:
* `-T <GTFS TRIP ID>` only calculate shape for a single trip (specified via its GTFS trip id) and output it as GeoJSON to
`<dbg-path>/path.json`
* `--write-graph` write the graph used for routing as GeoJSON to
`<dbg-path>/graph.json`
* `--write-cgraph` if `-T` is set, write the combination graph used for
routing as GeoJSON to `<dbg-path>/combgraph.json`
* `--write-trgraph` write the complete network graph to `<dbg-path>/trgraph.json`
# Configuration
A default configuration file `pfaedle.cfg` can be found in this repo and will be installed with `make install`. Custom configuration files can be specified with the `-c` flag. If no `-c` flag is set, `pfaedle` will parse and merge the following cfg files in the given order (if present): `<install prefix>/etc/pfaedle/pfaedle.cfg`, `$HOME/.config/pfaedle/pfaedle.cfg`, `<CWD>/pfaedle.cfg`. Values given in later files will overwrite earlier defined values.
# Evaluation
# Attribution
You may run an entire evaluation of our testing datasets Vitoria-Gasteiz, Paris, Switzerland and
Stuttgart with
```
mkdir build && cd build
cmake ..
make -j
make eval
```
*Notes:*
* this will download, and filter, the entire OSM files for Spain and the
Stuttgart region. Make sure you have enough space left on your hard drive.
* in evaluation mode, pfaedle needs significantly more time, because the
calculation of the similarity measurements between shapes are expensive
* if you are only interested in the end results of a single dataset, run
`make <dataset>.lighteval` in `/eval`. For example, `make paris.lighteval`
generates a shaped version of the paris dataset, without doing extensive
comparisons to the ground truth.
* similarily, if you want to run the extensive evaluation for a single dataset,
run `make <dataset>.eval` in `/eval`.
## Evaluation requirements
* zlib
On Debianesque systems, type
```
sudo apt-get install zlib1g-dev
```
to install the dependencies.
Note that the `shapes.txt` produced by `pfaedle` is based on OpenStreetMap data, which is licensed under ODbL 1.0 (see [here](https://osm.org/copyright)). If you copy, distribute, transmit or adapt the shapefied GTFS feed, please credit the contributors of OpenStreetMap.

30
action.yml Normal file
View file

@ -0,0 +1,30 @@
name: 'Generate shapes from OSM data'
description: 'Use pfaedle to generate shapes from OSM data for a GTFS feed.'
inputs:
gtfs_file:
description: 'Path to GTFS .zip file.'
required: true
osm_file:
description: 'Path to OSM .pbf file.'
required: true
mot:
description: 'Mode of transport to generate shapes for.'
required: false
default: 'all'
output_file:
description: 'Path to output GTFS .zip file.'
required: true
runs:
using: 'docker'
image: 'Dockerfile'
args:
- '-i'
- ${{ inputs.gtfs_file }}
- '-x'
- ${{ inputs.osm_file }}
- '-m'
- ${{ inputs.mot }}
- '-o'
- ${{ inputs.output_file }}

52
cmake/FindLibZip.cmake Normal file
View file

@ -0,0 +1,52 @@
# CMake module to search for libzip
#
# Once done this will define
#
# LIBZIP_FOUND - system has the zip library
# LIBZIP_INCLUDE_DIRS - the zip include directories
# LIBZIP_LIBRARY - Link this to use the zip library
#
# Copyright (c) 2017, Paul Blottiere, <paul.blottiere@oslandia.com>
# Copyright (c) 2017, Larry Shaffer, <lshaffer (at) boundlessgeo (dot) com>
# Add support for finding zipconf.h in separate location, e.g. on macOS
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
FIND_PATH(LIBZIP_INCLUDE_DIR
zip.h
"$ENV{LIB_DIR}/include"
"$ENV{INCLUDE}"
/usr/local/include
/usr/include
)
FIND_PATH(LIBZIP_CONF_INCLUDE_DIR
zipconf.h
"$ENV{LIB_DIR}/include"
"$ENV{LIB_DIR}/lib/libzip/include"
"$ENV{LIB}/lib/libzip/include"
/usr/local/lib/libzip/include
/usr/lib/libzip/include
/usr/local/include
/usr/include
"$ENV{INCLUDE}"
)
FIND_LIBRARY(LIBZIP_LIBRARY NAMES zip PATHS "$ENV{LIB_DIR}/lib" "$ENV{LIB}" /usr/local/lib /usr/lib )
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibZip DEFAULT_MSG
LIBZIP_LIBRARY LIBZIP_INCLUDE_DIR LIBZIP_CONF_INCLUDE_DIR)
SET(LIBZIP_INCLUDE_DIRS ${LIBZIP_INCLUDE_DIR} ${LIBZIP_CONF_INCLUDE_DIR})
MARK_AS_ADVANCED(LIBZIP_LIBRARY LIBZIP_INCLUDE_DIR LIBZIP_CONF_INCLUDE_DIR LIBZIP_INCLUDE_DIRS)
IF (LIBZIP_FOUND)
MESSAGE(STATUS "Found libzip: ${LIBZIP_LIBRARY}")
ELSE (LIBZIP_FOUND)
SET(LIBZIP_LIBRARY "")
SET(LIBZIP_INCLUDE_DIR "")
SET(LIBZIP_CONF_INCLUDE_DIR "")
MESSAGE(STATUS "Could not find libzip")
ENDIF (LIBZIP_FOUND)

View file

@ -1,133 +0,0 @@
#
# CMake module to C++ static analysis against
# Google C++ Style Guide (https://google.github.io/styleguide/cppguide.html)
#
# For more detials please follow links:
#
# - https://github.com/google/styleguide
# - https://pypi.python.org/pypi/cpplint
# - https://github.com/theandrewdavis/cpplint
#
# Copyright (c) 2016 Piotr L. Figlarek
#
# Usage
# -----
# Include this module via CMake include(...) command and then add each source directory
# via introduced by this module cpplint_add_subdirectory(...) function. Added directory
# will be recursivelly scanned and all available files will be checked.
#
# Example
# -------
# # include CMake module
# include(cmake/cpplint.cmake)
#
# # add all source code directories
# cpplint_add_subdirectory(core)
# cpplint_add_subdirectory(modules/c-bind)
#
# License (MIT)
# -------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# select files extensions to check
option(CPPLINT_TEST_C_FILES "Check *.c files" ON)
option(CPPLINT_TEST_H_FILES "Check *.h files" ON)
option(CPPLINT_TEST_CPP_FILES "Check *.cpp files" ON)
option(CPPLINT_TEST_HPP_FILES "Check *.hpp files" ON)
option(CPPLINT_TEST_TPP_FILES "Check *.tpp files" ON)
# target to run cpplint.py for all configured sources
set(CPPLINT_TARGET lint CACHE STRING "Name of C++ style checker target")
# project root directory
set(CPPLINT_PROJECT_ROOT "${PROJECT_SOURCE_DIR}" CACHE STRING "Project ROOT directory")
# find cpplint.py script
if(CPPLINT)
message(STATUS "cpplint parser: ${CPPLINT}")
else()
message(FATAL_ERROR "cpplint script: NOT FOUND! "
"Please set the CPPLINT variable.")
endif()
# common target to concatenate all cpplint.py targets
add_custom_target(${CPPLINT_TARGET} ALL)
# use cpplint.py to check source code files inside DIR directory
function(cpplint_add_subdirectory DIR)
# create relative path to the directory
set(ABSOLUTE_DIR ${CMAKE_CURRENT_LIST_DIR}/${DIR})
# add *.c files
if(CPPLINT_TEST_C_FILES)
set(EXTENSIONS ${EXTENSIONS}c,)
set(FILES_TO_CHECK ${FILES_TO_CHECK} ${ABSOLUTE_DIR}/*.c)
endif()
# add *.h files
if(CPPLINT_TEST_H_FILES)
set(EXTENSIONS ${EXTENSIONS}h,)
set(FILES_TO_CHECK ${FILES_TO_CHECK} ${ABSOLUTE_DIR}/*.h)
endif()
# add *.cpp files
if(CPPLINT_TEST_CPP_FILES)
set(EXTENSIONS ${EXTENSIONS}cpp,)
set(FILES_TO_CHECK ${FILES_TO_CHECK} ${ABSOLUTE_DIR}/*.cpp)
endif()
# add *.hpp files
if(CPPLINT_TEST_HPP_FILES)
set(EXTENSIONS ${EXTENSIONS}hpp,)
set(FILES_TO_CHECK ${FILES_TO_CHECK} ${ABSOLUTE_DIR}/*.hpp)
endif()
# add *.tpp files
if(CPPLINT_TEST_TPP_FILES)
set(EXTENSIONS ${EXTENSIONS}tpp,)
set(FILES_TO_CHECK ${FILES_TO_CHECK} ${ABSOLUTE_DIR}/*.tpp)
endif()
# find all source files inside project
file(GLOB_RECURSE LIST_OF_FILES ${FILES_TO_CHECK})
# create valid target name for this check
string(REGEX REPLACE "/" "." TEST_NAME ${DIR})
set(TARGET_NAME ${CPPLINT_TARGET}.${TEST_NAME})
# perform cpplint check
add_custom_target(${TARGET_NAME}
COMMAND ${CPPLINT} "--extensions=${EXTENSIONS}"
"--root=${CPPLINT_PROJECT_ROOT}"
"--quiet"
${LIST_OF_FILES}
DEPENDS ${LIST_OF_FILES}
COMMENT "cpplint: Checking source code style"
)
# run this target when root cpplint.py test is triggered
add_dependencies(${CPPLINT_TARGET} ${TARGET_NAME})
# add this test to CTest
add_test(${TARGET_NAME} ${CMAKE_MAKE_PROGRAM} ${TARGET_NAME})
endfunction()

6233
cpplint.py vendored

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,987 +0,0 @@
# Copyright 2018, University of Freiburg
# Chair of Algorithms and Datastructures
# Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
[rail]
# OSM entities to keep on different levels, as k=v. Applies
# to nodes, edges and relations.
# Nodes included in kept ways are always kept.
# Ways included in kept relations are always kept.
osm_filter_keep:
railway=rail
railway=light_rail
railway=narrow_gauge
route=rail
route=train
public_transport=stop_area|rel_flat
osm_filter_lvl1:
usage=branch
osm_filter_lvl2:
osm_filter_lvl3:
service=crossover
service=siding
# we cannot completely drop service=yard, because it is often used
# incorrectly for crossovers
service=yard
osm_filter_lvl4:
osm_filter_lvl5:
usage=industrial
usage=military
usage=test
service=spur
railway:traffic_mode=freight
# OSM entities to drop, as k=v. Applies to nodes, edges and
# relations.
# Nodes included in non-dropped ways are kept regardless of
# a matching drop filter.
# Ways included in non-dropped relations are kept regardless of
# a matching drop filter.
osm_filter_drop:
railway=abandoned
railway=construction
railway=disused
railway=miniature
railway=signal
railway=razed
railway=proposed
metro=yes
area=yes
# access=no
type=multipolygon
railway=platform
public_transport=platform
building=yes
building=train_station
amenity=shelter
amenity=bus_station
building=roof
# Nodes that should act as "no-hup" nodes. These are nodes
# that are contained in multiple ways, but cannot be used
# to switch from one way to another (for example, a
# track crossing in rail networks)
osm_filter_nohup:
railway:switch=no
railway=railway_crossing
# Edges that should act as one-way nodes.
osm_filter_oneway:
oneway=yes
railway:preferred_direction=forward
osm_filter_oneway_reverse:
railway:preferred_direction=backward
# Edges that may explicitely be used in
# both directions. May be used to set exception
# to "osm_filter_oneway"
osm_filter_undirected:
oneway=false
oneway=no
oneway=-1
railway:preferred_direction=both
railway:bidirectional=regular
# Nodes that are stations.
# Only nodes that have been kept during the filtering above will be
# checked.
osm_filter_station:
public_transport=stop_position
railway=stop
railway=halt
railway=station
#railway=tram_stop
railway=subway_stop
tram_stop=*
stop=*
# Relation fields that should be used for catching the lines that
# occur on an edge. Only relations that have been kept during the
# filtering above will be checked. The 'linename' will be normalized
# according to the rules in line_normalization_chain.
# The 'from_name' and 'to_name' will be normalized according to the
# rules in station_normalization_chain.
# The relations tags are given in the order of their relevance -
# the first normalized tag-value that is not null/empty will be
# taken.
osm_line_relation_tags:
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
[public_transport=stop_area]uic_ref=500
[public_transport=stop_area]wikidata=500
name=100
[public_transport=stop_area]name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 100, 200
# max edge level to which station will be snapped
osm_max_snap_level: 2
# sorted by priority, first found attr will be taken
osm_station_name_attrs:
name
[public_transport=stop_area]name
uic_name
# the track number tag in edges, first match is taken
osm_edge_track_number_tags:
railway:track_ref
local_ref
ref
# the track number tag in stop nodes, first match is taken,
# overwrites osm_edge_track_number_tags
osm_track_number_tags:
local_ref
ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.25
routing_lvl2_fac: 1.5
routing_lvl3_fac: 2
routing_lvl4_fac: 2.5
routing_lvl5_fac: 3.5
routing_lvl6_fac: 5
routing_lvl7_fac: 7
# Punishment (in meters) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 3000
routing_station_distance_punish_fac: 3.14
routing_non_osm_station_punish: 100
routing_platform_unmatched_punish: 2000
# Max angle that should be counted as a full turn
routing_full_turn_angle: 100
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 100
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 1
# special line normalization for trains
line_normalize_chain:
, -> ' ';
- -> ' ';
_ -> ' ';
" -> '';
' -> '';
` -> '';
/ -> ' ';
< -> ' ';
> -> ' ';
& -> '+';
ä -> ae;
ö -> oe;
ü -> ue;
ß -> ss;
è -> e;
é -> e;
á -> a;
à -> a;
ó -> o;
ò -> o;
í -> i;
ú -> u;
ù -> u;
ë -> e;
ç -> c;
å -> ae;
â -> a;
ê -> e;
ï -> i;
œ -> oe;
ø -> oe;
^line -> '';
^linie -> '';
^metro -> '';
^tram -> '';
^strassenbahn -> '';
^bus -> '';
# delete everything in brackets
\(.+\) -> ' ';
\[.+\] -> ' ';
# whitespace
\s+ -> ' ';
^\s -> '';
\s$ -> '';
# line/number combs ALWAYS with whitespace (ICE101 -> ICE 101)
^([a-zA-Z]+)([0-9]+)$ -> \1 \2;
# if a character line number is present, delete the numeric part
^([a-zA-Z]+) [0-9]+$ -> \1;
[bus]
# OSM entities to keep on different levels, as k=v. Applies
# to nodes, edges and relations.
# Nodes included in kept ways are always kept.
# Ways included in kept relations are always kept.
osm_filter_keep:
# highways
highway=motorway
highway=trunk
highway=primary
highway=secondary
highway=tertiary
highway=residential
highway=living_street
highway=unclassified
# highway links
highway=motorway_link
highway=trunk_link
highway=primary_link
highway=secondary_link
highway=tertiary_link
highway=residential_link
way=primary
way=seconday
way=bus_guideway
highway=bus_guideway
busway=*
psv=yes
psv=designated
trolley_wire=yes
trolleywire=yes
trolleybus=yes
trolley_bus=yes
route=bus
route=trolleybus
bus=yes
bus=designated
minibus=designated
minibus=yes
public_transport=stop_position
bus_stop=*
stop=*
highway=bus_stop
amenity=bus_station|no_match_ways|no_match_rels
# relations for the restriction system
type=restriction
type=restriction:bus
type=restriction:motorcar
osm_filter_lvl1:
highway=secondary
highway=secondary_link
bus=yes
bus=designated
minibus=yes
minibus=designated
psv=designated
psv=yes
access=psv
access=bus
trolley_wire=yes
trolleywire=yes
trolleybus=yes
trolley_bus=yes
psv=designated
osm_filter_lvl2:
highway=tertiary
highway=tertiary_link
osm_filter_lvl3:
highway=unclassified
highway=residential
highway=road
osm_filter_lvl4:
highway=living_street
highway=pedestrian
highway=service
psv=no
osm_filter_lvl5:
bus=no
service=siding
access=permissive
access=private
access=no
service=parking_aisle
highway=footway
# OSM entities to drop, as k=v. Applies to nodes, edges and
# relations.
# Nodes included in non-dropped ways are kept regardless of
# a matching drop filter.
# Ways included in non-dropped relations are kept regardless of
# a matching drop filter.
osm_filter_drop:
area=yes
train=yes|no_match_ways
# access=no
public_transport=stop_area|no_match_nds|no_match_rels
type=multipolygon
railway=platform
railway=station
# service=parking_aisle
highway=proposed
highway=footway
highway=construction
building=yes
building=train_station
leisure=garden
leisure=park
# Nodes that should act as "no-hup" nodes. These are nodes
# that are contained in multiple ways, but cannot be used
# to switch from one way to another (for example, a
# track crossing in rail networks)
osm_filter_nohup:
# Configuration of the OSM road restriction system
# We only support restriction with a single via node
# atm
osm_node_negative_restriction:
restriction=no_right_turn
restriction=no_left_turn
restriction=no_u_turn
restriction=no_straight_on
restriction:bus=no_right_turn
restriction:bus=no_left_turn
restriction:bus=no_u_turn
restriction:bus=no_straight_on
osm_node_positive_restriction:
restriction=only_left_turn
restriction=only_straight_on
restriction=only_right_turn
restriction:bus=only_left_turn
restriction:bus=only_straight_on
restriction:bus=only_right_turn
osm_filter_no_restriction:
except=psv|mult_val_match
except=bus|mult_val_match
# Edges that should act as one-way nodes.
osm_filter_oneway:
junction=roundabout # oneway=yes is implied
highway=motorway # oneway=yes is implied
oneway=yes
oneway=1
oneway=true
oneway:bus=yes
oneway:bus=1
oneway:bus=true
oneway:psv=yes
oneway:psv=1
oneway:psv=true
osm_filter_oneway_reverse:
oneway=-1
# Edges that may explicitely be used in
# both directions. May be used to set exception
# to "osm_filter_oneway"
osm_filter_undirected:
oneway=false
oneway=0
oneway=alternating
oneway=reversible
oneway=no
oneway:bus=no
oneway:bus=0
oneway:bus=false
oneway:psv=no
oneway:psv=0
oneway:psv=false
busway=opposite_lane
busway=opposite
busway:left=opposite_lane
busway:right=opposite_lane
psv=opposite_lane
psv=opposite
# Nodes that are stations.
# Only nodes that have been kept during the filtering above will be
# checked.
osm_filter_station:
public_transport=stop_position
bus_stop=*
stop=*
highway=bus_stop
amenity=bus_station
# Relation fields that should be used for catching the lines that
# occur on an edge. Only relations that have been kept during the
# filtering above will be checked. The 'linename' will be normalized
# according to the rules in line_normalization_chain.
# The 'from_name' and 'to_name' will be normalized according to the
# rules in station_normalization_chain.
# The relations tags are given in the order of their relevance -
# the first normalized tag-value that is not null/empty will be
# taken.
osm_line_relation_tags:
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10 , 50, 100
osm_max_snap_level: 5
osm_max_osm_station_distance: 7.5
# sorted by priority, first found attr will be taken
osm_station_name_attrs:
name
uic_name
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.25
routing_lvl2_fac: 1.5
routing_lvl3_fac: 1.75
routing_lvl4_fac: 2.25
routing_lvl5_fac: 3
routing_lvl6_fac: 4
routing_lvl7_fac: 5
# Punishment (in meters) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 500
routing_station_distance_punish_fac: 2.5
routing_non_osm_station_punish: 500
# Max angle that should be counted as a full turn
routing_full_turn_angle: 20
# Max angle in a route from a station to an already reachable neighbor
routing_snap_full_turn_angle: 110
osm_max_node_block_distance: 10
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 0
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 4
routing_one_way_edge_punish: 5000
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
# routing_line_unmatched_punish_fac: 1.75
[tram, subway, funicular]
# OSM entities to keep on different levels, as k=v. Applies
# to nodes, edges and relations.
# Nodes included in kept ways are always kept.
# Ways included in kept relations are always kept.
osm_filter_keep:
route=tram
railway=subway
railway=light_rail
railway=tram
railway=funicular
railway=station
railway=halt
railway=tram_stop
route=subway
route=light_rail
subway=yes
tram=yes
osm_filter_lv2:
service=siding
osm_filter_lvl5:
service=crossover
service=yard
# OSM entities to drop, as k=v. Applies to nodes, edges and
# relations.
# Nodes included in non-dropped ways are kept regardless of
# a matching drop filter.
# Ways included in non-dropped relations are kept regardless of
# a matching drop filter.
osm_filter_drop:
area=yes
public_transport=stop_area
type=multipolygon
railway=platform
public_transport=platform
service=alley
# Nodes that should act as "no-hup" nodes. These are nodes
# that are contained in multiple ways, but cannot be used
# to switch from one way to another (for example, a
# track crossing in rail networks)
osm_filter_nohup:
railway:switch=no
railway=railway_crossing
# Edges that should act as one-way nodes.
osm_filter_oneway:
oneway=yes
# Edges that may explicitely be used in
# both directions. May be used to set exception
# to "osm_filter_oneway"
osm_filter_undirected:
# Nodes that are stations.
# Only nodes that have been kept during the filtering above will be
# checked.
osm_filter_station:
public_transport=stop_position
station=subway
station=tram
railway=stop
railway=halt
railway=station
railway=tram_stop
railway=subway_stop
tram_stop=*
stop=*
# Relation fields that should be used for catching the lines that
# occur on an edge. Only relations that have been kept during the
# filtering above will be checked. The 'linename' will be normalized
# according to the rules in line_normalization_chain.
# The 'from_name' and 'to_name' will be normalized according to the
# rules in station_normalization_chain.
# The relations tags are given in the order of their relevance -
# the first normalized tag-value that is not null/empty will be
# taken.
osm_line_relation_tags:
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 50, 100
osm_max_snap_level: 4
# sorted by priority, first found attr will be taken
osm_station_name_attrs:
name
uic_name
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# Punishment (in meters) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 2000
routing_station_distance_punish_fac: 3.14
routing_non_osm_station_punish: 235
# Max angle that should be counted as a full turn
routing_full_turn_angle: 80
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 80
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
[ferry]
# OSM entities to keep on different levels, as k=v. Applies
# to nodes, edges and relations.
# Nodes included in kept ways are always kept.
# Ways included in kept relations are always kept.
osm_filter_keep:
route=ferry
waterway=river
motorboat=yes
ferry=yes
# Nodes that are stations.
# Only nodes that have been kept during the filtering above will be
# checked.
osm_filter_station:
public_transport=stop_position
station=ferry
railway=stop
railway=halt
railway=station
stop=*
# Relation fields that should be used for catching the lines that
# occur on an edge. Only relations that have been kept during the
# filtering above will be checked. The 'linename' will be normalized
# according to the rules in line_normalization_chain.
# The 'from_name' and 'to_name' will be normalized according to the
# rules in station_normalization_chain.
# The relations tags are given in the order of their relevance -
# the first normalized tag-value that is not null/empty will be
# taken.
osm_line_relation_tags:
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 100, 200
osm_max_snap_level: 4
# sorted by priority, first found attr will be taken
osm_station_name_attrs:
name
uic_name
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# Punishment (in meters) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 100
routing_station_distance_punish_fac: 3.14
routing_non_osm_station_punish: 50
# Max angle that should be counted as a full turn
routing_full_turn_angle: 45
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 0
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 0
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
[tram, bus, subway, rail, gondola, funicular, ferry]
# Regular expressions and station comparision is
# always case insensitive!
station_normalize_chain:
, -> ' ';
- -> ' ';
— -> ' ';
_ -> ' ';
" -> '';
' -> '';
` -> '';
\( -> ' ';
\) -> ' ';
\[ -> ' ';
\] -> ' ';
/ -> ' ';
'\\' -> ' ';
< -> ' ';
> -> ' ';
& -> '+';
ä -> ae;
ö -> oe;
ü -> ue;
ß -> ss;
è -> e;
é -> e;
á -> a;
à -> a;
ó -> o;
ò -> o;
ô -> o;
ç -> c;
í -> i;
ú -> u;
ù -> u;
ë -> e;
å -> ae;
â -> a;
ê -> e;
ï -> i;
œ -> oe;
ø -> oe;
str\. -> strasse;
av\. -> avenue;
# always separate 'street', 'strasse'
'([a-zA-Z])strasse($| )' -> '\1 strasse\2';
'([a-zA-Z])street($| )' -> '\1 street\2';
# always use "street"
'(^| )strasse($| )' -> '\1street\2';
# always use "avenue"
'(^| )avenida($| )' -> '\1avenue\2';
'(^| )avenu($| )' -> '\1avenue\2';
# normalize every possible abbr. of german "Bahnhof", "Hauptbahnhof", "Busbahnhof"
'(^| )hauptbf\.($| )' -> '\1hauptbahnhof\2';
'(^| )hauptbf($| )' -> '\1hauptbahnhof\2';
'(^| )hauptbhf\.($| )' -> '\1hauptbahnhof\2';
'(^| )hauptbhf($| )' -> '\1hauptbahnhof\2';
'(^| )zentraler busbahnhof($| )$' -> \1busbahnhof\2;
'(^| )zentraler omnibusbahnhof($| )$' -> \1busbahnhof\2;
'(^| )omnibusbahnhof($| )' -> '\1busbahnhof\2';
'(^| )omnibusbhf($| )' -> '\1busbahnhof\2';
'(^| )busbf\.($| )' -> '\1busbahnhof\2';
'(^| )busbf($| )' -> '\1busbahnhof\2';
'(^| )bus bf\.($| )' -> '\1busbahnhof\2';
'(^| )bus bf($| )' -> '\1busbahnhof\2';
'(^| )busbhf\.($| )' -> '\1busbahnhof\2';
'(^| )busbhf($| )' -> '\1busbahnhof\2';
'(^| )bus bhf\.($| )' -> '\1busbahnhof\2';
'(^| )bus bhf($| )' -> '\1busbahnhof\2';
'(^| )zob($| )' -> '\1busbahnhof\2';
'(^| )hbf\.($| )' -> '\1hauptbahnhof\2';
'(^| )hbf($| )' -> '\1hauptbahnhof\2';
'(^| )hb\.($| )' -> '\1hauptbahnhof\2';
'(^| )hb($| )' -> '\1hauptbahnhof\2';
'(^| )bf\.($| )' -> '\1bahnhof\2';
'(^| )bf($| )' -> '\1bahnhof\2';
'(^| )bhf\.($| )' -> '\1bahnhof\2';
'(^| )bhf($| )' -> '\1bahnhof\2';
'(^| )bhfeingang($| )' -> '\1bahnhofeingang\2';
'(^| )gare de($| )' -> '\1gare\2';
# if a stations starts with single station identifier
# always put it at the end (for example, "hauptbahnhof freiburg" becomes "freiburg hauptbahnhof")
'^hauptbahnhof (.+)$' -> \1 hauptbahnhof;
'^bahnhof (.+)$' -> \1 bahnhof;
'^busbahnhof (.+)$' -> \1 busbahnhof;
'^gare (.+)$' -> \1 gare;
'^station (.+)$' -> \1 station;
'(^| )busbahnhof($| )' -> '\1bbahnhof\2';
# normalize line types in station names
'(^| )u bahn\.($| )' -> '\1ubahn\2';
'(^| )metro\.($| )' -> '\1ubahn\2';
'(^| )subway\.($| )' -> '\1ubahn\2';
'(^| )underground\.($| )' -> '\1ubahn\2';
'(^| )ubahn($| )' -> '\1u\2';
'(^| )s bahn\.($| )' -> '\1sbahn\2';
'(^| )sbahn($| )' -> '\1s\2';
'(^| )tramway($| )' -> '\1tram\2';
'(^| )stadtbahn($| )' -> '\1tram\2';
'(^| )strassenbahn($| )' -> '\1tram\2';
'(^| )streetcar($| )' -> '\1tram\2';
'(^| )tram($| )' -> '\1t\2';
# delete track information from name
'(^| )kante [a-zA-Z0-9]{1,2}($| )' -> ' ';
'(^| )gleis [a-zA-Z0-9]{1,2}($| )' -> ' ';
'(^| )track [a-zA-Z0-9]{1,2}($| )' -> ' ';
'(^| )voie [a-zA-Z0-9]{1,2}($| )' -> ' ';
# abbrv
'(^| )und($| )' -> '\1+\2';
'(^| )and($| )' -> '\1+\2';
'(^| )et($| )' -> '\1+\2';
# noise
'\sde\s' -> ' ';
'\sda\s' -> ' ';
'\sdi\s' -> ' ';
'\sdel\s' -> ' ';
'\sdal\s' -> ' ';
# abbrv in most western languages
'(^| )saint ' -> '\1st. ';
'(^| )sankt ' -> '\1st. ';
'(^| )sanct ' -> '\1st. ';
\. -> ' ';
# whitespace
\s+ -> ' ';
^\s -> '';
\s$ -> '';
line_normalize_chain:
, -> ' ';
- -> ' ';
_ -> ' ';
" -> '';
' -> '';
` -> '';
/ -> ' ';
< -> ' ';
> -> ' ';
& -> '+';
ä -> ae;
ö -> oe;
ü -> ue;
ß -> ss;
è -> e;
é -> e;
á -> a;
à -> a;
ó -> o;
ò -> o;
í -> i;
ú -> u;
ù -> u;
ë -> e;
å -> ae;
ç -> c;
â -> a;
ê -> e;
ï -> i;
œ -> oe;
ø -> oe;
^line -> '';
^linie -> '';
^metro -> '';
^tram -> '';
^strassenbahn -> '';
^bus -> '';
# delete everything in brackets
\(.+\) -> ' ';
\[.+\] -> ' ';
# whitespace
\s+ -> ' ';
^\s -> '';
\s$ -> '';
# line/number combs ALWAYS without whitespace (T 2 -> T2)
^([a-zA-Z]+) ([0-9]+)$ -> \1\2;
track_normalize_chain:
'(^| )gleis($| )' -> '';
'(^| )gl\.($| )' -> '';
'(^| )platform($| )' -> '';
'(^| )track($| )' -> '';
'(^| )rail($| )' -> '';
# line/number combs ALWAYS without whitespace (1 A -> 1A)
^([a-zA-Z]+) ([0-9]+)$ -> \1\2;
^([0-9]+) ([a-zA-Z]+)$ -> \1\2;
# delete track numbers greater than 999
^[0-9]{4,}$ -> '';

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,12 @@
# Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
[tram, bus, coach, subway, rail, gondola, funicular, ferry]
routing_transition_penalty_fac: 0.0083
routing_station_move_penalty_fac: 0.0039
station_similarity_classification_method: jaccard-geodist
# Regular expressions and station comparision is
# always case insensitive!
station_normalize_chain:
@ -25,6 +31,12 @@ station_normalize_chain:
Ä -> Ae;
Ö -> Oe;
Ü -> Ue;
À -> A;
Ò -> O;
Ù -> U;
Á -> A;
Ó -> O;
Ú -> U;
ä -> ae;
ö -> oe;
ü -> ue;
@ -199,6 +211,9 @@ line_normalize_chain:
# line/number combs ALWAYS without whitespace (T 2 -> T2)
^([a-zA-Z]+) ([0-9]+)$ -> \1\2;
# delete extra line specifier in data for Vitoria-Gasteiz (L2 -> 2)
^l([0-9a-zA-Z]+)$ -> \1;
track_normalize_chain:
'(^| )gleis($| )' -> '';
'(^| )gl\.($| )' -> '';
@ -334,22 +349,9 @@ osm_line_relation_tags:
from_name=from
to_name=to
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
[public_transport=stop_area]uic_ref=500
[public_transport=stop_area]wikidata=500
name=100
[public_transport=stop_area]name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 100, 200
# max distance in meters between a snapped position on an
# edge and the input GTFS/OSM station
osm_max_snap_distance: 200
# max edge level to which station will be snapped
osm_max_snap_level: 2
@ -372,24 +374,28 @@ osm_track_number_tags:
local_ref
ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.25
routing_lvl2_fac: 1.5
routing_lvl3_fac: 2
routing_lvl4_fac: 2.5
routing_lvl5_fac: 3.5
routing_lvl6_fac: 5
routing_lvl7_fac: 7
# avg speed on segment levels, in km/h
osm_lvl0_avg_speed: 120 # default level
osm_lvl1_avg_speed: 90
osm_lvl2_avg_speed: 65
osm_lvl3_avg_speed: 50
osm_lvl4_avg_speed: 30
osm_lvl5_avg_speed: 20
osm_lvl6_avg_speed: 10
osm_lvl7_avg_speed: 5
# Punishment (in meters) to add to the distance
# Punishment (in seconds) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 3000
routing_full_turn_penalty: 180 # 3 minutes
routing_station_distance_punish_fac: 3.14
# Penalty added to non-station placements
routing_non_station_penalty: 0.4
routing_non_osm_station_punish: 100
# If the station name does not match, add this penalty
routing_station_unmatched_penalty: 0.4
routing_platform_unmatched_punish: 2000
# If the platform does not match, add this penalty
routing_platform_unmatched_penalty: 0.1
# Max angle that should be counted as a full turn
routing_full_turn_angle: 100
@ -397,19 +403,25 @@ routing_full_turn_angle: 100
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 100
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Factor by which the vehicle slows down in a one way street (factor 5
# means it will take 5 times longer)
osm_one_way_speed_penalty_fac: 5
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# Additional one-time time penalty for entering a one-way segment
# in seconds
osm_one_way_entry_cost: 300
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 1
# If a segment has no matching line attributes, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
routing_line_unmatched_time_penalty_fac: 1.2
routing_line_station_to_unmatched_time_penalty: 1.1
routing_line_station_from_unmatched_time_penalty: 1.05
# If a segment has no line attributes at all, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
# routing_no_lines_penalty_fac: 1
# special line normalization for trains
line_normalize_chain:
@ -553,6 +565,12 @@ osm_filter_keep:
type=restriction:motorcar
osm_filter_lvl1:
highway=trunk
highway=trunk_link
highway=primary
highway=primary_link
osm_filter_lvl2:
highway=secondary
highway=secondary_link
bus=yes
@ -569,22 +587,22 @@ osm_filter_lvl1:
trolley_bus=yes
psv=designated
osm_filter_lvl2:
osm_filter_lvl3:
highway=tertiary
highway=tertiary_link
osm_filter_lvl3:
osm_filter_lvl4:
highway=unclassified
highway=residential
highway=road
osm_filter_lvl4:
osm_filter_lvl5:
highway=living_street
highway=pedestrian
highway=service
psv=no
osm_filter_lvl5:
osm_filter_lvl6:
bus=no
service=siding
access=permissive
@ -592,6 +610,7 @@ osm_filter_lvl5:
access=no
service=parking_aisle
highway=footway
highway=track
# OSM entities to drop, as k=v. Applies to nodes, edges and
# relations.
@ -709,6 +728,12 @@ osm_filter_station:
highway=bus_stop
amenity=bus_station
osm_filter_turning_circle:
highway=turning_circle
highway=turning_loop
junction=roundabout
highway=mini_roundabout
# Relation fields that should be used for catching the lines that
# occur on an edge. Only relations that have been kept during the
# filtering above will be checked. The 'linename' will be normalized
@ -722,28 +747,18 @@ osm_line_relation_tags:
line_name=ref,name # careful, no space after/before comma allowed!
from_name=from
to_name=to
line_color=colour,color
# max distance in meters between a OSM station candidate
# and the input GTFS station
osm_max_station_cand_distance: 200
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 50, 100
osm_max_snap_fallback_distance: 300
# max distance in meters between a snapped position on an
# edge and the input GTFS/OSM station
osm_max_snap_distance: 100
osm_max_snap_level: 5
osm_max_osm_station_distance: 8.0
# sorted by priority, first found attr will be taken
osm_station_name_attrs:
name
@ -752,22 +767,45 @@ osm_station_name_attrs:
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.25
routing_lvl2_fac: 1.5
routing_lvl3_fac: 1.75
routing_lvl4_fac: 2.25
routing_lvl5_fac: 3
routing_lvl6_fac: 4
routing_lvl7_fac: 5
# avg speed on segment levels, in km/h
osm_lvl0_avg_speed: 85 # default level
osm_lvl1_avg_speed: 70
osm_lvl2_avg_speed: 55
osm_lvl3_avg_speed: 40
osm_lvl4_avg_speed: 30
osm_lvl5_avg_speed: 20
osm_lvl6_avg_speed: 10
osm_lvl7_avg_speed: 5
# Punishment (in meters) to add to the distance
# Factor by which the vehicle slows down in a one way street (factor 5
# means it will take 5 times longer)
osm_one_way_speed_penalty_fac: 5
# Additional one-time time penalty for entering a one-way segment
# in seconds
osm_one_way_entry_cost: 300
# If a segment has no matching line attributes, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
routing_line_unmatched_time_penalty_fac: 1.2
routing_line_station_to_unmatched_time_penalty: 1.1
routing_line_station_from_unmatched_time_penalty: 1.05
# If a segment has no line attributes at all, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
# routing_no_lines_penalty_fac: 1
# If the station name does not match, add this penalty
routing_station_unmatched_penalty: 0.4
# Punishment (in seconds) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 500
routing_full_turn_penalty: 120 # 2 minutes
routing_station_distance_punish_fac: 2.5
routing_non_osm_station_punish: 500
# Penalty added to non-station placements
routing_non_station_penalty: 0.4
# Max angle that should be counted as a full turn
routing_full_turn_angle: 20
@ -777,21 +815,6 @@ routing_snap_full_turn_angle: 110
osm_max_node_block_distance: 10
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 0
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 4
routing_one_way_edge_punish: 5000
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
# routing_line_unmatched_punish_fac: 1.75
[coach]
@ -840,14 +863,14 @@ osm_filter_lvl7:
service=parking_aisle
highway=footway
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.15
routing_lvl2_fac: 1.5
routing_lvl3_fac: 1.75
routing_lvl4_fac: 2.25
routing_lvl5_fac: 2.5
routing_lvl6_fac: 3
routing_lvl7_fac: 4
osm_lvl0_avg_speed: 120 # default level
osm_lvl1_avg_speed: 90
osm_lvl2_avg_speed: 65
osm_lvl3_avg_speed: 50
osm_lvl4_avg_speed: 30
osm_lvl5_avg_speed: 20
osm_lvl6_avg_speed: 10
osm_lvl7_avg_speed: 5
osm_max_snap_level: 5
@ -948,20 +971,9 @@ osm_line_relation_tags:
from_name=from
to_name=to
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 50, 100
# max distance in meters between a snapped position on an
# edge and the input GTFS/OSM station
osm_max_snap_distance: 100
osm_max_snap_level: 4
@ -974,22 +986,25 @@ osm_station_name_attrs:
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# avg speed on segment levels, in km/h
osm_lvl0_avg_speed: 85 # default level
osm_lvl1_avg_speed: 70
osm_lvl2_avg_speed: 55
osm_lvl3_avg_speed: 40
osm_lvl4_avg_speed: 30
osm_lvl5_avg_speed: 20
osm_lvl6_avg_speed: 10
osm_lvl7_avg_speed: 5
# Punishment (in meters) to add to the distance
# Punishment (in seconds) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 2000
routing_full_turn_penalty: 180 # 3 minutes
routing_station_distance_punish_fac: 3.14
# Penalty added to non-station placements
routing_non_station_penalty: 0.4
routing_non_osm_station_punish: 235
# If the station name does not match, add this penalty
routing_station_unmatched_penalty: 0.4
# Max angle that should be counted as a full turn
routing_full_turn_angle: 80
@ -997,19 +1012,21 @@ routing_full_turn_angle: 80
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 80
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Factor by which the vehicle slows down in a one way street (factor 5
# means it will take 5 times longer)
osm_one_way_speed_penalty_fac: 2
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# If a segment has no matching line attributes, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
routing_line_unmatched_time_penalty_fac: 1.2
routing_line_station_to_unmatched_time_penalty: 1.1
routing_line_station_from_unmatched_time_penalty: 1.05
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
# If a segment has no line attributes at all, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
# routing_no_lines_penalty_fac: 1
[gondola]
@ -1089,20 +1106,9 @@ osm_line_relation_tags:
from_name=from
to_name=to
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 50, 100
# max distance in meters between a snapped position on an
# edge and the input GTFS/OSM station
osm_max_snap_distance: 100
osm_max_snap_level: 4
@ -1115,22 +1121,25 @@ osm_station_name_attrs:
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# avg speed on segment levels, in km/h
osm_lvl0_avg_speed: 85 # default level
osm_lvl1_avg_speed: 70
osm_lvl2_avg_speed: 55
osm_lvl3_avg_speed: 40
osm_lvl4_avg_speed: 30
osm_lvl5_avg_speed: 20
osm_lvl6_avg_speed: 10
osm_lvl7_avg_speed: 5
# Punishment (in meters) to add to the distance
# Punishment (in seconds) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 2000
routing_full_turn_penalty: 120 # 2 minutes
routing_station_distance_punish_fac: 3.14
# Penalty added to non-station placements
routing_non_station_penalty: 0.4
routing_non_osm_station_punish: 235
# If the station name does not match, add this penalty
routing_station_unmatched_penalty: 0.4
# Max angle that should be counted as a full turn
routing_full_turn_angle: 80
@ -1138,19 +1147,21 @@ routing_full_turn_angle: 80
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 80
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Factor by which the vehicle slows down in a one way street (factor 5
# means it will take 5 times longer)
osm_one_way_speed_penalty_fac: 2
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# If a segment has no matching line attributes, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
routing_line_unmatched_time_penalty_fac: 1.2
routing_line_station_to_unmatched_time_penalty: 1.1
routing_line_station_from_unmatched_time_penalty: 1.05
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
# If a segment has no line attributes at all, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
# routing_no_lines_penalty_fac: 1
[funicular]
@ -1261,20 +1272,9 @@ osm_line_relation_tags:
from_name=from
to_name=to
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 50, 100
# max distance in meters between a snapped position on an
# edge and the input GTFS/OSM station
osm_max_snap_distance: 100
osm_max_snap_level: 4
@ -1287,22 +1287,25 @@ osm_station_name_attrs:
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# avg speed on segment levels, in km/h
osm_lvl0_avg_speed: 85 # default level
osm_lvl1_avg_speed: 70
osm_lvl2_avg_speed: 55
osm_lvl3_avg_speed: 40
osm_lvl4_avg_speed: 30
osm_lvl5_avg_speed: 20
osm_lvl6_avg_speed: 10
osm_lvl7_avg_speed: 5
# Punishment (in meters) to add to the distance
# Punishment (in seconds) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 2000
routing_full_turn_penalty: 120 # 2 minutes
routing_station_distance_punish_fac: 3.14
# Penalty added to non-station placements
routing_non_station_penalty: 0.4
routing_non_osm_station_punish: 235
# If the station name does not match, add this penalty
routing_station_unmatched_penalty: 0.4
# Max angle that should be counted as a full turn
routing_full_turn_angle: 80
@ -1310,19 +1313,21 @@ routing_full_turn_angle: 80
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 80
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 100
# Factor by which the vehicle slows down in a one way street (factor 5
# means it will take 5 times longer)
osm_one_way_speed_penalty_fac: 2
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# If a segment has no matching line attributes, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
routing_line_unmatched_time_penalty_fac: 1.2
routing_line_station_to_unmatched_time_penalty: 1.1
routing_line_station_from_unmatched_time_penalty: 1.05
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
# If a segment has no line attributes at all, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
# routing_no_lines_penalty_fac: 1
[ferry]
@ -1336,12 +1341,18 @@ osm_filter_keep:
waterway=river
motorboat=yes
ferry=yes
amenity=ferry_terminal
mooring=ferry
station=ferry
# Nodes that are stations.
# Only nodes that have been kept during the filtering above will be
# checked.
osm_filter_station:
ferry=yes
public_transport=stop_position
amenity=ferry_terminal
mooring=ferry
station=ferry
railway=stop
railway=halt
@ -1362,20 +1373,9 @@ osm_line_relation_tags:
from_name=from
to_name=to
# attr name together with the
# max distance in meters between any of the groups members and
# a potential new member
# first matching rule will be taken
# only applies to nodes that match osm_filter_station!
osm_station_group_attrs:
uic_ref=500
wikidata=500
name=100
# max distance in meters between a snapped station position and the
# original station position
osm_max_snap_distance: 10, 100, 200
# max distance in meters between a snapped position on an
# edge and the input GTFS/OSM station
osm_max_snap_distance: 500
osm_max_snap_level: 4
@ -1388,22 +1388,25 @@ osm_station_name_attrs:
# the track number tag in stop nodes, first one is taken
osm_track_number_tags: local_ref
routing_lvl0_fac: 1 # default level
routing_lvl1_fac: 1.5
routing_lvl2_fac: 2
routing_lvl3_fac: 2.5
routing_lvl4_fac: 3.5
routing_lvl5_fac: 5
routing_lvl6_fac: 5
routing_lvl7_fac: 5
# avg speed on segment levels, in km/h
osm_lvl0_avg_speed: 70 # default level
osm_lvl1_avg_speed: 60
osm_lvl2_avg_speed: 50
osm_lvl3_avg_speed: 35
osm_lvl4_avg_speed: 30
osm_lvl5_avg_speed: 25
osm_lvl6_avg_speed: 10
osm_lvl7_avg_speed: 5
# Punishment (in meters) to add to the distance
# Punishment (in seconds) to add to the distance
# function if a vehicle performans a full turn
routing_full_turn_punish: 100
routing_full_turn_penalty: 120 # 2 minutes
routing_station_distance_punish_fac: 3.14
# Penalty added to non-station placements
routing_non_station_penalty: 0.4
routing_non_osm_station_punish: 50
# If the station name does not match, add this penalty
routing_station_unmatched_penalty: 0.4
# Max angle that should be counted as a full turn
routing_full_turn_angle: 45
@ -1411,17 +1414,18 @@ routing_full_turn_angle: 45
# Max angle in a route from a station to an already reachable neighbar
routing_snap_full_turn_angle: 0
# Punishment (in meters) to add to the distance
# function if a vehicle passes a station node without
# stopping there
routing_pass_thru_station_punish: 0
# Factor by which the vehicle slows down in a one way street (factor 5
# means it will take 5 times longer)
osm_one_way_speed_penalty_fac: 2
# Punishment factor for every meter a vehicle
# travels through a one-way edge
routing_one_way_meter_punish_fac: 1
# Punishment factor for every meter a vehicle
# travels through an edge without any matching line
# information
routing_line_unmatched_punish_fac: 0.5
# If a segment has no matching line attributes, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
routing_line_unmatched_time_penalty_fac: 1.2
routing_line_station_to_unmatched_time_penalty: 1.15
routing_line_station_from_unmatched_time_penalty: 1.1
# If a segment has no line attributes at all, multiply the
# time needed to traverse it with the given factor (should
# be > 1 for a punishment, values < 1 will prefer unmatching segments)
# routing_no_lines_penalty_fac: 1

View file

@ -1,15 +1,25 @@
set(PFAEDLE_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR})
if (COMMAND cpplint_add_subdirectory)
cpplint_add_subdirectory(pfaedle)
endif()
include_directories(
${PFAEDLE_INCLUDE_DIR}
)
if (ZLIB_FOUND)
include_directories( ${ZLIB_INCLUDE_DIRS} )
endif( ZLIB_FOUND )
if (LIBZIP_FOUND)
include_directories( SYSTEM ${LIBZIP_INCLUDE_DIR} )
include_directories( SYSTEM ${LIBZIP_CONF_INCLUDE_DIR} )
endif( LIBZIP_FOUND )
if (BZIP2_FOUND)
include_directories( SYSTEM ${BZIP2_INCLUDE_DIR} )
endif( BZIP2_FOUND )
add_subdirectory(util)
add_subdirectory(pfaedle)
add_subdirectory(cppgtfs)
add_subdirectory(xml)
add_subdirectory(configparser)
add_subdirectory(shapevl)
add_subdirectory(xml)

@ -1 +1 @@
Subproject commit 54da0925a2a15ddaed209f67a6aa5d7a4cd65426
Subproject commit ca166b3446d5bb8b5fb8c6f637ca3f9cb0a8ff3b

@ -1 +1 @@
Subproject commit 8c256e6e1ae8ed61639700e90802ccbefbcd4c34
Subproject commit d26d5794d396141905d71ecb8cd4f45e0120cba7

View file

@ -16,5 +16,16 @@ configure_file (
add_executable(pfaedle ${pfaedle_main})
add_library(pfaedle_dep ${pfaedle_SRC})
include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src)
target_link_libraries(pfaedle pfaedle_dep util xml configparser ad_cppgtfs ${Boost_LIBRARIES} -lpthread)
include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/xml/include/ ${PROJECT_SOURCE_DIR}/src/cppgtfs/src)
target_link_libraries(pfaedle pfaedle_dep util configparser ad_cppgtfs -lpthread)
if (LIBZIP_FOUND)
target_link_libraries(pfaedle ${LIBZIP_LIBRARY})
endif( LIBZIP_FOUND )
if (BZIP2_FOUND)
target_link_libraries(pfaedle ${BZIP2_LIBRARY})
endif( BZIP2_FOUND )
add_subdirectory(tests)

View file

@ -5,6 +5,10 @@
#ifndef PFAEDLE_DEF_H_
#define PFAEDLE_DEF_H_
#include <unistd.h>
#include <string>
#include "util/log/Log.h"
#include "util/Misc.h"
#include "util/geo/Geo.h"
#include "util/geo/PolyLine.h"
@ -13,17 +17,17 @@
#define __str_c(s) s ## 1
#define __str_d(s) __str_c(s)
#if !defined(PFAEDLE_PRECISION) || (__str_d(PFAEDLE_PRECISION) == 1)
#undef PFAEDLE_PRECISION
#define PFAEDLE_PRECISION double
#if !defined(PFDL_PREC) || (__str_d(PFDL_PREC) == 1)
#undef PFDL_PREC
#define PFDL_PREC double
#endif
#define PFAEDLE_PRECISION_STR __str_a(PFAEDLE_PRECISION)
#define PFDL_PREC_STR __str_a(PFDL_PREC)
#define POINT util::geo::Point<PFAEDLE_PRECISION>
#define LINE util::geo::Line<PFAEDLE_PRECISION>
#define BOX util::geo::Box<PFAEDLE_PRECISION>
#define POLYLINE util::geo::PolyLine<PFAEDLE_PRECISION>
#define POINT util::geo::Point<PFDL_PREC>
#define LINE util::geo::Line<PFDL_PREC>
#define BOX util::geo::Box<PFDL_PREC>
#define POLYLINE util::geo::PolyLine<PFDL_PREC>
#define BOX_PADDING 2500

View file

@ -9,38 +9,66 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <clocale>
#include <fstream>
#include <map>
#include <string>
#include <vector>
#include "ad/cppgtfs/Parser.h"
#include "ad/cppgtfs/Writer.h"
#include "pfaedle/config/ConfigReader.h"
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/MotConfigReader.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/gtfs/Writer.h"
#include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/OsmIdSet.h"
#include "pfaedle/router/ShapeBuilder.h"
#include "pfaedle/router/Stats.h"
#include "pfaedle/statsimi-classifier/StatsimiClassifier.h"
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/trgraph/StatGroup.h"
#include "util/Misc.h"
#include "util/geo/output/GeoGraphJsonOutput.h"
#include "util/geo/output/GeoJsonOutput.h"
#include "util/json/Writer.h"
#include "util/log/Log.h"
using pfaedle::router::MOTs;
#ifndef CFG_HOME_SUFFIX
#define CFG_HOME_SUFFIX "/.config"
#endif
#ifndef CFG_DIR
#define CFG_DIR "/etc"
#endif
#ifndef CFG_FILE_NAME
#define CFG_FILE_NAME "pfaedle.cfg"
#endif
using configparser::ParseFileExc;
using pfaedle::config::Config;
using pfaedle::config::ConfigReader;
using pfaedle::config::MotConfig;
using pfaedle::config::MotConfigReader;
using pfaedle::osm::BBoxIdx;
using pfaedle::osm::OsmBuilder;
using pfaedle::config::MotConfig;
using pfaedle::config::Config;
using pfaedle::router::DistDiffTransWeight;
using pfaedle::router::DistDiffTransWeightNoHeur;
using pfaedle::router::ExpoTransWeight;
using pfaedle::router::ExpoTransWeightNoHeur;
using pfaedle::router::MOTs;
using pfaedle::router::NormDistrTransWeight;
using pfaedle::router::NormDistrTransWeightNoHeur;
using pfaedle::router::Router;
using pfaedle::router::RouterImpl;
using pfaedle::router::ShapeBuilder;
using configparser::ParseFileExc;
using pfaedle::config::MotConfigReader;
using pfaedle::config::ConfigReader;
using pfaedle::eval::Collector;
using pfaedle::router::Stats;
using pfaedle::statsimiclassifier::BTSClassifier;
using pfaedle::statsimiclassifier::EDClassifier;
using pfaedle::statsimiclassifier::JaccardClassifier;
using pfaedle::statsimiclassifier::JaccardGeodistClassifier;
using pfaedle::statsimiclassifier::PEDClassifier;
using pfaedle::statsimiclassifier::StatsimiClassifier;
enum class RetCode {
SUCCESS = 0,
@ -58,6 +86,9 @@ enum class RetCode {
std::string getFileNameMotStr(const MOTs& mots);
std::vector<std::string> getCfgPaths(const Config& cfg);
// _____________________________________________________________________________
void gtfsWarnCb(std::string msg) { LOG(WARN) << msg; }
// _____________________________________________________________________________
int main(int argc, char** argv) {
// disable output buffering for standard output
@ -66,6 +97,11 @@ int main(int argc, char** argv) {
// initialize randomness
srand(time(NULL) + rand()); // NOLINT
// use utf8 locale
std::setlocale(LC_ALL, "en_US.utf8");
T_START(total);
Config cfg;
MotConfigReader motCfgReader;
@ -73,20 +109,18 @@ int main(int argc, char** argv) {
cr.read(&cfg, argc, argv);
std::vector<pfaedle::gtfs::Feed> gtfs(cfg.feedPaths.size());
// feed containing the shapes in memory for evaluation
ad::cppgtfs::gtfs::Feed evalFeed;
std::vector<std::string> cfgPaths = getCfgPaths(cfg);
try {
motCfgReader.parse(cfgPaths);
motCfgReader.parse(cfgPaths, cfg.motCfgParam);
} catch (const configparser::ParseExc& ex) {
LOG(ERROR) << "Could not parse MOT configurations, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::MOT_CFG_PARSE_ERR));
}
if (cfg.osmPath.empty() && !cfg.writeOverpass) {
if (cfg.osmPath.empty() && !cfg.writeOverpass && !cfg.writeOsmfilter) {
std::cerr << "No OSM input file specified (-x), see --help." << std::endl;
exit(static_cast<int>(RetCode::NO_OSM_INPUT));
}
@ -97,42 +131,42 @@ int main(int argc, char** argv) {
exit(static_cast<int>(RetCode::NO_MOT_CFG));
}
T_START(gtfsBuild);
if (cfg.feedPaths.size() == 1) {
if (cfg.inPlace) cfg.outputPath = cfg.feedPaths[0];
if (!cfg.writeOverpass)
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
if (!cfg.writeOverpass && !cfg.writeOsmfilter)
LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[0] << " ...";
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]);
}
ad::cppgtfs::Parser p(cfg.feedPaths[0], false,
cfg.parseAdditionalGTFSFields,
cfg.verbosity ? gtfsWarnCb : 0);
p.parse(&gtfs[0]);
} catch (const ad::cppgtfs::ParserException& ex) {
LOG(ERROR) << "Could not parse input GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_PARSE_ERR));
}
if (!cfg.writeOverpass) LOG(INFO) << "Done.";
} else if (cfg.writeOsm.size() || cfg.writeOverpass) {
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
if (!cfg.writeOverpass)
LOG(INFO) << "Reading " << cfg.feedPaths[i] << " ...";
ad::cppgtfs::Parser p;
if (!cfg.writeOverpass && !cfg.writeOsmfilter)
LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[i] << " ...";
try {
p.parse(&gtfs[i], cfg.feedPaths[i]);
ad::cppgtfs::Parser p(cfg.feedPaths[i]);
p.parse(&gtfs[i]);
} catch (const ad::cppgtfs::ParserException& ex) {
LOG(ERROR) << "Could not parse input GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_PARSE_ERR));
}
if (!cfg.writeOverpass) LOG(INFO) << "Done.";
}
} else if (cfg.feedPaths.size() > 1) {
std::cerr << "Multiple feeds only allowed in filter mode." << std::endl;
exit(static_cast<int>(RetCode::MULT_FEEDS_NOT_ALWD));
}
auto tGtfsBuild = T_STOP(gtfsBuild);
LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size()
<< " unique MOT configs.";
MOTs cmdCfgMots = cfg.mots;
@ -150,12 +184,20 @@ int main(int argc, char** argv) {
}
}
double maxSpeed = 0;
for (const auto& c : motCfgReader.getConfigs()) {
if (c.osmBuildOpts.maxSpeed > maxSpeed) {
maxSpeed = c.osmBuildOpts.maxSpeed;
}
}
if (cfg.writeOsm.size()) {
LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ...";
BBoxIdx box(BOX_PADDING);
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
&box);
&box, maxSpeed, 0, cfg.verbosity);
}
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
@ -167,7 +209,7 @@ int main(int argc, char** argv) {
}
try {
osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
} catch (const xml::XmlFileException& ex) {
} catch (const pfxml::parse_exc& ex) {
LOG(ERROR) << "Could not parse OSM data, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::OSM_PARSE_ERR));
@ -177,7 +219,7 @@ int main(int argc, char** argv) {
BBoxIdx box(BOX_PADDING);
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
&box);
&box, maxSpeed, 0, cfg.verbosity);
}
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
@ -189,15 +231,27 @@ int main(int argc, char** argv) {
}
osmBuilder.overpassQryWrite(&std::cout, opts, box);
exit(static_cast<int>(RetCode::SUCCESS));
} else if (cfg.writeOsmfilter) {
BBoxIdx box(BOX_PADDING);
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.osmfilterRuleWrite(&std::cout, opts, box);
exit(static_cast<int>(RetCode::SUCCESS));
} else if (!cfg.feedPaths.size()) {
std::cout << "No input feed specified, see --help" << std::endl;
exit(static_cast<int>(RetCode::NO_INPUT_FEED));
}
std::vector<double> dfBins;
auto dfBinStrings = util::split(std::string(cfg.evalDfBins), ',');
for (auto st : dfBinStrings) dfBins.push_back(atof(st.c_str()));
Collector ecoll(cfg.evalPath, dfBins);
Stats stats;
double tOsmBuild = 0;
std::map<std::string, std::pair<size_t, size_t>> graphDimensions;
std::vector<double> hopDists;
for (const auto& motCfg : motCfgReader.getConfigs()) {
std::string filePost;
@ -209,7 +263,7 @@ int main(int argc, char** argv) {
filePost = getFileNameMotStr(usedMots);
std::string motStr = pfaedle::router::getMotStr(usedMots);
LOG(INFO) << "Calculating shapes for mots " << motStr;
LOG(INFO) << "Matching shapes for mots " << motStr;
try {
pfaedle::router::FeedStops fStops =
@ -220,26 +274,89 @@ int main(int argc, char** argv) {
pfaedle::osm::OsmBuilder osmBuilder;
pfaedle::osm::BBoxIdx box(BOX_PADDING);
ShapeBuilder::getGtfsBox(&gtfs[0], cmdCfgMots, cfg.shapeTripId,
cfg.dropShapes, &box);
ShapeBuilder::getGtfsBox(
&gtfs[0], usedMots, cfg.shapeTripId, cfg.dropShapes, &box,
motCfg.osmBuildOpts.maxSpeed, &hopDists, cfg.verbosity);
T_START(osmBuild);
if (fStops.size())
osmBuilder.read(cfg.osmPath, motCfg.osmBuildOpts, &graph, box,
cfg.gridSize, &fStops, &restr);
cfg.gridSize, &restr);
// TODO(patrick): move this somewhere else
for (auto& feedStop : fStops) {
if (feedStop.second) {
feedStop.second->pl().getSI()->getGroup()->writePens(
motCfg.osmBuildOpts.trackNormzer,
motCfg.routingOpts.platformUnmatchedPen,
motCfg.routingOpts.stationDistPenFactor,
motCfg.routingOpts.nonOsmPen);
}
tOsmBuild += T_STOP(osmBuild);
graphDimensions[filePost].first = graph.getNds().size();
for (const auto& nd : graph.getNds()) {
graphDimensions[filePost].second += nd->getAdjListOut().size();
}
ShapeBuilder shapeBuilder(&gtfs[0], &evalFeed, cmdCfgMots, motCfg, &ecoll,
&graph, &fStops, &restr, cfg);
StatsimiClassifier* statsimiClassifier;
if (motCfg.routingOpts.statsimiMethod == "bts") {
statsimiClassifier = new BTSClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "jaccard") {
statsimiClassifier = new JaccardClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "jaccard-geodist") {
statsimiClassifier = new JaccardGeodistClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "ed") {
statsimiClassifier = new EDClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "ped") {
statsimiClassifier = new PEDClassifier();
} else {
LOG(ERROR) << "Unknown station similarity classifier "
<< motCfg.routingOpts.statsimiMethod;
exit(1);
}
Router* router = 0;
if (motCfg.routingOpts.transPenMethod == "exp") {
if (cfg.noAStar)
router = new RouterImpl<ExpoTransWeightNoHeur>();
else
router = new RouterImpl<ExpoTransWeight>();
} else if (motCfg.routingOpts.transPenMethod == "distdiff") {
if (cfg.noAStar)
router = new RouterImpl<DistDiffTransWeightNoHeur>();
else
router = new RouterImpl<DistDiffTransWeight>();
} else if (motCfg.routingOpts.transPenMethod == "timenorm") {
if (cfg.noAStar)
router = new RouterImpl<NormDistrTransWeightNoHeur>();
else
router = new RouterImpl<NormDistrTransWeight>();
} else {
LOG(ERROR) << "Unknown routing method "
<< motCfg.routingOpts.transPenMethod;
exit(1);
}
ShapeBuilder shapeBuilder(&gtfs[0], usedMots, motCfg, &graph, &fStops,
&restr, statsimiClassifier, router, cfg);
pfaedle::netgraph::Graph ng;
if (singleTrip) {
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
util::geo::output::GeoJsonOutput o(pstr);
auto l = shapeBuilder.shapeL(singleTrip);
stats += l.second;
LOG(INFO) << "Outputting path.json...";
// reproject to WGS84 to match RFC 7946
o.print(l.first, {});
o.flush();
pstr.close();
} else {
stats += shapeBuilder.shapeify(&ng);
}
if (router) delete router;
if (statsimiClassifier) delete statsimiClassifier;
if (cfg.writeGraph) {
LOG(INFO) << "Outputting graph.json...";
@ -250,49 +367,76 @@ int main(int argc, char** argv) {
fstr.close();
}
if (singleTrip) {
LOG(INFO) << "Outputting path.json...";
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
util::geo::output::GeoJsonOutput o(pstr);
auto l = shapeBuilder.shapeL(singleTrip);
o.print(l, util::json::Dict{{"ver", "new"}});
o.flush();
pstr.close();
exit(static_cast<int>(RetCode::SUCCESS));
}
pfaedle::netgraph::Graph ng;
shapeBuilder.shape(&ng);
if (singleTrip) exit(static_cast<int>(RetCode::SUCCESS));
if (cfg.buildTransitGraph) {
util::geo::output::GeoGraphJsonOutput out;
LOG(INFO) << "Outputting trgraph" + filePost + ".json...";
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");
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph-" + filePost +
".json");
out.print(ng, fstr);
fstr.close();
}
} catch (const xml::XmlFileException& ex) {
} catch (const pfxml::parse_exc& 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);
// outputting stats
if (cfg.writeStats) {
util::json::Dict graphSizes;
double numNodesTot = 0;
double numEdgesTot = 0;
for (const auto& gd : graphDimensions) {
util::json::Dict a;
a["num_nodes"] = gd.second.first;
a["num_edges"] = gd.second.second;
numNodesTot += gd.second.first;
numEdgesTot += gd.second.second;
graphSizes[gd.first] = a;
}
double hopDistSum = 0;
for (auto d : hopDists) hopDistSum += d;
util::json::Dict jsonStats = {
{"statistics",
util::json::Dict{
{"gtfs_num_stations", gtfs[0].getStops().size()},
{"gtfs_num_trips", gtfs[0].getTrips().size()},
{"gtfs_avg_hop_dist", hopDistSum / (hopDists.size() * 1.0)},
{"graph_dimension", graphSizes},
{"num_nodes_tot", numNodesTot},
{"num_edges_tot", numEdgesTot},
{"num_tries", stats.numTries},
{"num_trie_leafs", stats.numTrieLeafs},
{"dijkstra_iters", stats.dijkstraIters},
{"time_solve", stats.solveTime},
{"time_read_osm", tOsmBuild},
{"time_read_gtfs", tGtfsBuild},
{"time_tot", T_STOP(total)},
{"peak-memory", util::readableSize(util::getPeakRSS())},
{"peak-memory-bytes", util::getPeakRSS()}}}};
std::ofstream ofs;
ofs.open(cfg.dbgOutputPath + "/stats.json");
util::json::Writer wr(&ofs, 10, true);
wr.val(jsonStats);
wr.closeAll();
}
if (cfg.feedPaths.size()) {
try {
mkdir(cfg.outputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ...";
pfaedle::gtfs::Writer w;
w.write(&gtfs[0], cfg.outputPath);
} catch (const ad::cppgtfs::WriterException& ex) {
LOG(ERROR) << "Could not write final GTFS feed, reason was:";
LOG(ERROR) << "Could not write output GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_WRITE_ERR));
}
@ -303,9 +447,27 @@ int main(int argc, char** argv) {
// _____________________________________________________________________________
std::string getFileNameMotStr(const MOTs& mots) {
MOTs tmp = mots;
std::string motStr;
for (const auto& mot : mots) {
motStr += "-" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot);
std::string names[11] = {"tram", "subway", "rail", "bus",
"ferry", "cablecar", "gondola", "funicular",
"coach", "trolleybus", "monorail"};
for (const auto& n : names) {
const auto& types = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(n);
const auto& isect = pfaedle::router::motISect(tmp, types);
if (isect.size() == types.size()) {
if (motStr.size()) motStr += "-";
motStr += n;
for (const auto& mot : isect) tmp.erase(mot);
}
}
for (const auto& mot : tmp) {
if (motStr.size()) motStr += "-";
motStr += ad::cppgtfs::gtfs::flat::Route::getTypeString(mot);
}
return motStr;
@ -316,29 +478,10 @@ 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;
auto path = std::string(INSTALL_PREFIX) + std::string(CFG_DIR) + "/" +
"pfaedle" + "/" + CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
@ -350,8 +493,8 @@ std::vector<std::string> getCfgPaths(const Config& cfg) {
// local user configuration path, if available
{
auto path = std::string(homedir) + XDG_CONFIG_HOME_SUFFIX + "/" +
"pfaedle" + "/" + CFG_FILE_NAME;
auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + "pfaedle" + "/" +
CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
@ -361,7 +504,7 @@ std::vector<std::string> getCfgPaths(const Config& cfg) {
}
}
if (buf) free(buf);
// free this here, as we use homedir in the block above
// CWD
{

View file

@ -10,16 +10,18 @@
#include "pfaedle/Def.h"
#include "pfaedle/_config.h"
#include "pfaedle/config/ConfigReader.h"
#include "pfaedle/config/PfaedleConfig.h"
#include "util/String.h"
#include "util/geo/Geo.h"
#include "util/log/Log.h"
using pfaedle::config::ConfigReader;
using std::string;
using std::exception;
using std::string;
using std::vector;
static const char* YEAR = __DATE__ + 7;
static const char* YEAR = &__DATE__[7];
static const char* COPY =
"University of Freiburg - Chair of Algorithms and Data Structures";
static const char* AUTHORS = "Patrick Brosi <brosi@informatik.uni-freiburg.de>";
@ -28,11 +30,10 @@ static const char* AUTHORS = "Patrick Brosi <brosi@informatik.uni-freiburg.de>";
void ConfigReader::help(const char* bin) {
std::cout << std::setfill(' ') << std::left << "pfaedle GTFS map matcher "
<< VERSION_FULL << "\n(built " << __DATE__ << " " << __TIME__
<< " with geometry precision <" << PFAEDLE_PRECISION_STR << ">)\n\n"
<< " with geometry precision <" << PFDL_PREC_STR << ">)\n\n"
<< "(C) " << YEAR << " " << COPY << "\n"
<< "Authors: " << AUTHORS << "\n\n"
<< "Usage: " << bin
<< " -x <OSM FILE> <GTFS FEED>\n\n"
<< "Usage: " << bin << " -x <OSM FILE> <GTFS FEED>\n\n"
<< "Allowed options:\n\n"
<< "General:\n"
<< std::setw(35) << " -v [ --version ]"
@ -43,6 +44,8 @@ void ConfigReader::help(const char* bin) {
<< "drop shapes already present in the feed and\n"
<< std::setw(35) << " "
<< " recalculate them\n"
<< std::setw(35) << " --write-colors"
<< "write matched route line colors, where missing\n"
<< "\nInput:\n"
<< std::setw(35) << " -c [ --config ] arg"
<< "pfaedle config file\n"
@ -62,7 +65,11 @@ void ConfigReader::help(const char* bin) {
<< std::setw(35) << " "
<< " ferry | boat | ship, cablecar, gondola,\n"
<< std::setw(35) << " "
<< " funicular, coach} or as GTFS mot codes\n"
<< " funicular, coach, mono-rail | monorail,\n"
<< std::setw(35) << " "
<< " trolley | trolleybus | trolley-bus} or\n"
<< std::setw(35) << " "
<< " as GTFS mot codes\n"
<< "\nOutput:\n"
<< std::setw(35) << " -o [ --output ] arg (=gtfs-out)"
<< "GTFS output path\n"
@ -83,26 +90,6 @@ void ConfigReader::help(const char* bin) {
<< "write routing graph as GeoJSON to\n"
<< std::setw(35) << " "
<< " <dbg-path>/graph.json\n"
<< std::setw(35) << " --write-cgraph"
<< "if -T is set, write combination graph as\n"
<< std::setw(35) << " "
<< " GeoJSON to "
"<dbg-path>/combgraph.json\n"
<< std::setw(35) << " --method arg (=global)"
<< "matching method to use, either 'global'\n"
<< std::setw(35) << " "
<< " (based on HMM), 'greedy' or "
"'greedy2'\n"
<< std::setw(35) << " --eval"
<< "evaluate existing shapes against matched\n"
<< std::setw(35) << " "
<< " shapes and print results\n"
<< std::setw(35) << " --eval-path arg (=.)"
<< "path for eval file output\n"
<< std::setw(35) << " --eval-df-bins arg (= )"
<< "bins to use for d_f histogram, comma sep.\n"
<< std::setw(35) << " "
<< " (e.g. 10,20,30,40)\n"
<< "\nMisc:\n"
<< std::setw(35) << " -T [ --trip-id ] arg"
<< "Do routing only for trip <arg>, write result \n"
@ -110,12 +97,24 @@ void ConfigReader::help(const char* bin) {
<< " to <dbg-path>/path.json\n"
<< std::setw(35) << " --overpass"
<< "Output overpass query for matching OSM data\n"
<< std::setw(35) << " --osmfilter"
<< "Output osmfilter filter rules for matching OSM data\n"
<< std::setw(35) << " --grid-size arg (=2000)"
<< "Grid cell size\n"
<< std::setw(35) << " --use-route-cache"
<< "(experimental) cache intermediate routing\n"
<< std::setw(35) << " "
<< " results\n";
<< "Approx. grid cell size in meters\n"
<< std::setw(35) << " --no-fast-hops"
<< "Disable fast hops technique\n"
<< std::setw(35) << " --no-a-star"
<< "Disable A* heuristic \n"
<< std::setw(35) << " --no-trie"
<< "Disable trip tries \n"
<< std::setw(35) << " --no-hop-cache"
<< "Disable hop cache \n"
<< std::setw(35) << " --stats"
<< "write stats to stats.json\n"
<< std::setw(35) << " -W [ --warn ]"
<< "enable verbose warning messages\n"
<< std::setw(35) << " -P"
<< "additional parameter string (in cfg file format)\n";
}
// _____________________________________________________________________________
@ -131,49 +130,44 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
{"mots", required_argument, NULL, 'm'},
{"grid-size", required_argument, 0, 'g'},
{"overpass", no_argument, 0, 'a'},
{"osmfilter", no_argument, 0, 'f'},
{"osm-out", required_argument, 0, 'X'},
{"trip-id", required_argument, 0, 'T'},
{"write-graph", no_argument, 0, 1},
{"write-cgraph", no_argument, 0, 2},
{"write-trgraph", no_argument, 0, 4},
{"method", required_argument, 0, 5},
{"eval", no_argument, 0, 3},
{"eval-path", required_argument, 0, 6},
{"eval-df-bins", required_argument, 0, 7},
{"dbg-path", required_argument, 0, 'd'},
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"inplace", no_argument, 0, 9},
{"use-route-cache", no_argument, 0, 8},
{"no-fast-hops", no_argument, 0, 10},
{"no-a-star", no_argument, 0, 11},
{"no-trie", no_argument, 0, 12},
{"write-colors", no_argument, 0, 13},
{"stats", no_argument, 0, 14},
{"no-hop-cache", no_argument, 0, 15},
{"gaussian-noise", required_argument, 0, 16},
{"warn", no_argument, 0, 'W'},
{"keep-additional-gtfs-fields", no_argument, 0, 'F'},
{0, 0, 0, 0}};
char c;
while ((c = getopt_long(argc, argv, ":o:hvi:c:x:Dm:g:X:T:d:p", ops, 0)) !=
int c;
while ((c = getopt_long(argc, argv, ":o:hvi:c:x:Dm:g:X:T:d:pP:FW", ops, 0)) !=
-1) {
switch (c) {
case 1:
cfg->writeGraph = true;
break;
case 2:
cfg->writeCombGraph = true;
break;
case 3:
cfg->evaluate = true;
break;
case 4:
cfg->buildTransitGraph = true;
break;
case 5:
cfg->solveMethod = optarg;
case 10:
cfg->noFastHops = true;
break;
case 6:
cfg->evalPath = optarg;
case 11:
cfg->noAStar = true;
break;
case 7:
cfg->evalDfBins = optarg;
break;
case 8:
cfg->useCaching = true;
case 12:
cfg->noTrie = true;
break;
case 'o':
cfg->outputPath = optarg;
@ -194,7 +188,7 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
motStr = optarg;
break;
case 'g':
cfg->gridSize = atof(optarg);
cfg->gridSize = atof(optarg) / util::geo::M_PER_DEG;
break;
case 'X':
cfg->writeOsm = optarg;
@ -202,22 +196,41 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
case 'T':
cfg->shapeTripId = optarg;
break;
case 'P':
cfg->motCfgParam += std::string("\n") + optarg;
break;
case 'd':
cfg->dbgOutputPath = optarg;
break;
case 'a':
cfg->writeOverpass = true;
break;
case 'f':
cfg->writeOsmfilter = true;
break;
case 9:
cfg->inPlace = true;
break;
case 13:
cfg->writeColors = true;
break;
case 14:
cfg->writeStats = true;
break;
case 15:
cfg->noHopCache = true;
break;
case 16:
cfg->gaussianNoise = atof(optarg);
break;
case 'W':
cfg->verbosity = 1;
break;
case 'F':
cfg->parseAdditionalGTFSFields = true;
break;
case 'v':
std::cout << "pfaedle " << VERSION_FULL << " (built " << __DATE__ << " "
<< __TIME__ << " with geometry precision <"
<< PFAEDLE_PRECISION_STR << ">)\n"
<< "(C) " << YEAR << " " << COPY << "\n"
<< "Authors: " << AUTHORS << "\nGNU General Public "
"License v3.0\n";
std::cout << "pfaedle " << VERSION_FULL << std::endl;
exit(0);
case 'p':
printOpts = true;

View file

@ -17,20 +17,11 @@ struct MotConfig {
router::MOTs mots;
osm::OsmReadOpts osmBuildOpts;
router::RoutingOpts routingOpts;
std::map<std::string, std::string> unproced;
std::string transWeight;
};
inline bool operator==(const MotConfig& a, const MotConfig& b) {
bool unprocedEq = a.unproced.size() == b.unproced.size();
for (const auto& kv : a.unproced) {
if (!b.unproced.count(kv.first) ||
b.unproced.find(kv.first)->second != kv.second) {
unprocedEq = false;
break;
}
}
return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts &&
unprocedEq;
return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts;
}
} // namespace config

View file

@ -2,28 +2,33 @@
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <limits>
#include <set>
#include <string>
#include "pfaedle/config/MotConfigReader.h"
#include "pfaedle/osm/OsmReadOpts.h"
#include "util/Misc.h"
#include "util/String.h"
#include "util/log/Log.h"
using pfaedle::config::MotConfigReader;
using pfaedle::config::MotConfig;
using pfaedle::osm::FilterRule;
using pfaedle::osm::KeyVal;
using ad::cppgtfs::gtfs::Route;
using configparser::ConfigFileParser;
using configparser::ParseExc;
using pfaedle::config::MotConfig;
using pfaedle::config::MotConfigReader;
using pfaedle::osm::DeepAttrRule;
using pfaedle::osm::FilterRule;
using pfaedle::osm::KeyVal;
using pfaedle::trgraph::ReplRules;
using ad::cppgtfs::gtfs::Route;
double DEF_TRANS_PEN = 0.0083;
// _____________________________________________________________________________
MotConfigReader::MotConfigReader() {}
// _____________________________________________________________________________
void MotConfigReader::parse(const std::vector<std::string>& paths) {
void MotConfigReader::parse(const std::vector<std::string>& paths,
const std::string& literal) {
ConfigFileParser p;
// parse explicitely given paths
@ -32,17 +37,47 @@ void MotConfigReader::parse(const std::vector<std::string>& paths) {
p.parse(s);
}
if (literal.size()) p.parseStr(literal);
for (const auto& sec : p.getSecs()) {
MotConfig curCfg;
MotConfig cfg;
cfg.transWeight = "expo";
std::string secStr = sec.first;
if (secStr.empty()) continue;
std::set<std::string> procedKeys;
if (p.hasKey(secStr, "routing_emission_method")) {
cfg.routingOpts.emPenMethod =
p.getStr(secStr, "routing_emission_method");
} else {
cfg.routingOpts.emPenMethod = "exp";
}
if (p.hasKey(secStr, "routing_transition_method")) {
cfg.routingOpts.transPenMethod =
p.getStr(secStr, "routing_transition_method");
} else {
cfg.routingOpts.transPenMethod = "exp";
}
if (p.hasKey(secStr, "station_similarity_classification_method")) {
cfg.routingOpts.statsimiMethod =
p.getStr(secStr, "station_similarity_classification_method");
} else {
cfg.routingOpts.statsimiMethod = "jaccard-geodist";
}
if (p.hasKey(secStr, "routing_use_stations")) {
cfg.routingOpts.useStations = p.getBool(secStr, "routing_use_stations");
} else {
cfg.routingOpts.useStations = true;
}
if (p.hasKey(secStr, "osm_filter_keep")) {
procedKeys.insert("osm_filter_keep");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_keep", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
@ -50,315 +85,468 @@ void MotConfigReader::parse(const std::vector<std::string>& paths) {
for (uint8_t i = 0; i < 8; i++) {
std::string name = std::string("osm_filter_lvl") + std::to_string(i);
if (p.hasKey(secStr, name)) {
procedKeys.insert(name);
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
cfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
}
if (p.hasKey(secStr, "osm_filter_drop")) {
procedKeys.insert("osm_filter_drop");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.dropFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.dropFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_max_snap_level")) {
procedKeys.insert("osm_max_snap_level");
curCfg.osmBuildOpts.maxSnapLevel =
p.getInt(sec.first, "osm_max_snap_level");
cfg.osmBuildOpts.maxSnapLevel = p.getInt(sec.first, "osm_max_snap_level");
} else {
curCfg.osmBuildOpts.maxSnapLevel = 7;
cfg.osmBuildOpts.maxSnapLevel = 7;
}
if (p.hasKey(secStr, "osm_filter_nohup")) {
procedKeys.insert("osm_filter_nohup");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_nohup", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_oneway")) {
procedKeys.insert("osm_filter_oneway");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_oneway", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_oneway_reverse")) {
procedKeys.insert("osm_filter_oneway_reverse");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
cfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_undirected")) {
procedKeys.insert("osm_filter_undirected");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_undirected", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_station")) {
procedKeys.insert("osm_filter_station");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_station", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_station_blocker")) {
procedKeys.insert("osm_filter_station_blocker");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_turning_circle")) {
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_turning_circle", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.turnCycleFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_node_positive_restriction")) {
procedKeys.insert("osm_node_positive_restriction");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
cfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_node_negative_restriction")) {
procedKeys.insert("osm_node_negative_restriction");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
cfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_no_restriction")) {
procedKeys.insert("osm_filter_no_restriction");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) {
auto fRule = getFRule(kvs);
curCfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
cfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_station_name_attrs")) {
procedKeys.insert("osm_station_name_attrs");
for (const std::string& r :
p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) {
curCfg.osmBuildOpts.statAttrRules.nameRule.push_back(
getDeepAttrRule(r));
cfg.osmBuildOpts.statAttrRules.nameRule.push_back(getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_track_number_tags")) {
procedKeys.insert("osm_track_number_tags");
for (const std::string& r :
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
curCfg.osmBuildOpts.statAttrRules.platformRule.push_back(
cfg.osmBuildOpts.statAttrRules.platformRule.push_back(
getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_station_id_attrs")) {
procedKeys.insert("osm_station_id_attrs");
for (const std::string& r :
p.getStrArr(sec.first, "osm_station_id_attrs", ' ')) {
curCfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r));
cfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_edge_track_number_tags")) {
procedKeys.insert("osm_edge_track_number_tags");
for (const std::string& r :
p.getStrArr(sec.first, "osm_edge_track_number_tags", ' ')) {
curCfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
cfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_station_group_attrs")) {
procedKeys.insert("osm_station_group_attrs");
auto arr = p.getStrArr(secStr, "osm_station_group_attrs", ' ');
LOG(WARN) << "Option osm_station_group_attrs has been removed.";
}
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});
}
}
// default value, to enable color writing on old configs
cfg.osmBuildOpts.relLinerules.colorRule = {"colour", "color"};
if (p.hasKey(secStr, "osm_line_relation_tags")) {
procedKeys.insert("osm_line_relation_tags");
auto arr = p.getStrArr(secStr, "osm_line_relation_tags", ' ');
for (const auto& ruleStr : arr) {
auto rule = getKv(ruleStr);
auto tags = util::split(rule.second, ',');
if (rule.first == "from_name")
curCfg.osmBuildOpts.relLinerules.fromNameRule = tags;
cfg.osmBuildOpts.relLinerules.fromNameRule = tags;
else if (rule.first == "to_name")
curCfg.osmBuildOpts.relLinerules.toNameRule = tags;
cfg.osmBuildOpts.relLinerules.toNameRule = tags;
else if (rule.first == "line_name")
curCfg.osmBuildOpts.relLinerules.sNameRule = tags;
cfg.osmBuildOpts.relLinerules.sNameRule = tags;
else if (rule.first == "line_color")
cfg.osmBuildOpts.relLinerules.colorRule = tags;
}
}
cfg.osmBuildOpts.maxSnapDistance = 50;
if (p.hasKey(secStr, "osm_max_snap_distance")) {
procedKeys.insert("osm_max_snap_distance");
curCfg.osmBuildOpts.maxSnapDistances =
p.getDoubleArr(secStr, "osm_max_snap_distance", ',');
} else {
curCfg.osmBuildOpts.maxSnapDistances.push_back(50);
auto v = p.getDoubleArr(secStr, "osm_max_snap_distance", ',');
if (v.size()) cfg.osmBuildOpts.maxSnapDistance = v.back();
}
cfg.osmBuildOpts.maxStationCandDistance =
cfg.osmBuildOpts.maxSnapDistance * 2;
if (p.hasKey(secStr, "osm_max_station_cand_distance")) {
auto v = p.getDouble(secStr, "osm_max_station_cand_distance");
cfg.osmBuildOpts.maxStationCandDistance = v;
}
if (p.hasKey(secStr, "osm_max_snap_fallback_distance")) {
procedKeys.insert("osm_max_snap_fallback_distance");
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
p.getDouble(secStr, "osm_max_snap_fallback_distance");
} else {
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.end()) *
2;
LOG(WARN) << "Option osm_max_snap_fallback_distance has been removed.";
}
if (p.hasKey(secStr, "osm_max_osm_station_distance")) {
procedKeys.insert("osm_max_osm_station_distance");
curCfg.osmBuildOpts.maxOsmStationDistance =
p.getDouble(secStr, "osm_max_osm_station_distance");
double ref = p.getDouble(secStr, "osm_max_osm_station_distance");
cfg.osmBuildOpts.maxOsmStationDistances.push_back(ref);
} else {
curCfg.osmBuildOpts.maxOsmStationDistance = 5;
cfg.osmBuildOpts.maxOsmStationDistances.push_back(15);
}
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
procedKeys.insert("osm_max_node_block_distance");
curCfg.osmBuildOpts.maxBlockDistance =
cfg.osmBuildOpts.maxBlockDistance =
p.getDouble(secStr, "osm_max_node_block_distance");
} else {
curCfg.osmBuildOpts.maxBlockDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.end()) /
cfg.osmBuildOpts.maxBlockDistance =
*std::max_element(cfg.osmBuildOpts.maxOsmStationDistances.begin(),
cfg.osmBuildOpts.maxOsmStationDistances.end()) /
8;
}
double DEF_SPEED = 85;
for (uint8_t i = 0; i < 8; i++) {
std::string name =
std::string("routing_lvl") + std::to_string(i) + "_fac";
if (p.hasKey(secStr, name)) {
procedKeys.insert(name);
double v = p.getDouble(sec.first, name);
curCfg.routingOpts.levelPunish[i] = v;
} else {
curCfg.routingOpts.levelPunish[i] = 1;
double f = p.getPosDouble(sec.first, name);
LOG(WARN) << "Option " << name << " is deprecated, use osm_lvl"
<< std::to_string(i) << "_avg_speed instead.";
double v = DEF_SPEED / f;
LOG(DEBUG) << " (using osm_lvl" << std::to_string(i) << "_avg_speed of "
<< v << " instead)";
cfg.osmBuildOpts.levelDefSpeed[i] = v * 0.2777; // store in m/s
}
}
if (p.hasKey(secStr, "routing_full_turn_punish")) {
procedKeys.insert("routing_full_turn_punish");
curCfg.routingOpts.fullTurnPunishFac =
p.getDouble(secStr, "routing_full_turn_punish");
for (uint8_t i = 0; i < 8; i++) {
std::string name =
std::string("osm_lvl") + std::to_string(i) + "_avg_speed";
if (p.hasKey(secStr, name)) {
double v = p.getPosDouble(sec.first, name);
cfg.osmBuildOpts.levelDefSpeed[i] = v * 0.2777; // store in m/s
}
if (p.hasKey(secStr, "routing_no_self_hops")) {
procedKeys.insert("routing_no_self_hops");
curCfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops");
}
if (p.hasKey(secStr, "routing_full_turn_angle")) {
procedKeys.insert("routing_full_turn_angle");
double ang = p.getDouble(secStr, "routing_full_turn_angle");
curCfg.routingOpts.fullTurnAngle = ang;
curCfg.osmBuildOpts.fullTurnAngle = ang;
} else {
curCfg.routingOpts.fullTurnAngle = 5;
curCfg.osmBuildOpts.fullTurnAngle = 5;
}
if (p.hasKey(secStr, "routing_snap_full_turn_angle")) {
procedKeys.insert("routing_snap_full_turn_angle");
double ang = p.getDouble(secStr, "routing_snap_full_turn_angle");
curCfg.osmBuildOpts.maxAngleSnapReach = ang;
} else {
curCfg.osmBuildOpts.maxAngleSnapReach = curCfg.routingOpts.fullTurnAngle;
}
if (p.hasKey(secStr, "routing_pass_thru_station_punish")) {
procedKeys.insert("routing_pass_thru_station_punish");
curCfg.routingOpts.passThruStationsPunish =
p.getDouble(secStr, "routing_pass_thru_station_punish");
}
if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) {
procedKeys.insert("routing_one_way_meter_punish_fac");
curCfg.routingOpts.oneWayPunishFac =
p.getDouble(secStr, "routing_one_way_meter_punish_fac");
LOG(WARN) << "Option routing_one_way_meter_punish_fac is deprecated, use "
"osm_one_way_speed_penalty_fac instead.";
cfg.osmBuildOpts.oneWaySpeedPen =
1 + p.getPosDouble(secStr, "routing_one_way_meter_punish_fac");
LOG(DEBUG) << " (using osm_one_way_speed_penalty_fac of "
<< cfg.osmBuildOpts.oneWaySpeedPen << " instead)";
} else {
cfg.osmBuildOpts.oneWaySpeedPen = 1;
}
if (p.hasKey(secStr, "routing_one_way_edge_punish")) {
procedKeys.insert("routing_one_way_edge_punish");
curCfg.routingOpts.oneWayEdgePunish =
p.getDouble(secStr, "routing_one_way_edge_punish");
if (p.hasKey(secStr, "osm_one_way_speed_penalty_fac")) {
cfg.osmBuildOpts.oneWaySpeedPen =
p.getPosDouble(secStr, "osm_one_way_speed_penalty_fac");
} else {
// def already set above
}
if (p.hasKey(secStr, "osm_one_way_entry_cost")) {
cfg.osmBuildOpts.oneWayEntryCost =
p.getPosDouble(secStr, "osm_one_way_entry_cost");
} else {
cfg.osmBuildOpts.oneWayEntryCost = 0;
}
// take the same cost for taking restricted turns to keep
// configuration simple
double val = cfg.osmBuildOpts.oneWayEntryCost * 10.0;
if (val > std::numeric_limits<uint32_t>::max()) {
val = std::numeric_limits<uint32_t>::max();
}
cfg.routingOpts.turnRestrCost = val;
if (p.hasKey(secStr, "routing_full_turn_punish")) {
double val = p.getPosDouble(secStr, "routing_full_turn_punish");
LOG(WARN) << "Option routing_full_turn_punish is deprecated, use "
"routing_full_turn_penalty instead.";
val /= cfg.osmBuildOpts.levelDefSpeed[0];
LOG(DEBUG) << " (using routing_full_turn_penalty of " << val
<< " instead)";
val *= 10.0;
if (val > std::numeric_limits<uint32_t>::max()) {
val = std::numeric_limits<uint32_t>::max();
}
cfg.routingOpts.fullTurnPunishFac = val;
}
if (p.hasKey(secStr, "routing_full_turn_penalty")) {
double val = p.getPosDouble(secStr, "routing_full_turn_penalty") * 10.0;
if (val > std::numeric_limits<uint32_t>::max()) {
val = std::numeric_limits<uint32_t>::max();
}
cfg.routingOpts.fullTurnPunishFac = val;
}
if (p.hasKey(secStr, "routing_no_self_hops")) {
cfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops");
}
if (p.hasKey(secStr, "routing_full_turn_angle")) {
double ang = p.getPosDouble(secStr, "routing_full_turn_angle");
cfg.routingOpts.fullTurnAngle = ang;
cfg.osmBuildOpts.fullTurnAngle = ang;
} else {
cfg.routingOpts.fullTurnAngle = 5;
cfg.osmBuildOpts.fullTurnAngle = 5;
}
if (p.hasKey(secStr, "routing_snap_full_turn_angle")) {
double ang = p.getPosDouble(secStr, "routing_snap_full_turn_angle");
cfg.osmBuildOpts.maxAngleSnapReach = ang;
} else {
cfg.osmBuildOpts.maxAngleSnapReach = cfg.routingOpts.fullTurnAngle;
}
if (p.hasKey(secStr, "routing_pass_thru_station_punish")) {
LOG(WARN) << "Option routing_pass_thru_station_punish has been removed.";
}
cfg.routingOpts.turnRestrCost *= 10.0;
if (p.hasKey(secStr, "routing_no_lines_punish_fac")) {
LOG(WARN) << "Option routing_no_lines_punish_fac is deprecated, use "
"routing_no_lines_penalty_fac instead.";
cfg.routingOpts.noLinesPunishFact =
1 + p.getPosDouble(secStr, "routing_no_lines_punish_fac");
LOG(DEBUG) << " (using routing_no_lines_penalty_fac of "
<< cfg.routingOpts.noLinesPunishFact << " instead)";
} else {
cfg.routingOpts.noLinesPunishFact = 1;
}
if (p.hasKey(secStr, "routing_no_lines_penalty_fac")) {
cfg.routingOpts.noLinesPunishFact =
p.getPosDouble(secStr, "routing_no_lines_penalty_fac");
} else {
// default already set above
}
// store this at two places, as we are writing the punishment into the graph
cfg.osmBuildOpts.noLinesPunishFact = cfg.routingOpts.noLinesPunishFact;
if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) {
procedKeys.insert("routing_line_unmatched_punish_fac");
curCfg.routingOpts.lineUnmatchedPunishFact =
p.getDouble(secStr, "routing_line_unmatched_punish_fac");
LOG(WARN)
<< "Option routing_line_unmatched_punish_fac is deprecated, use "
"routing_line_unmatched_time_penalty_fac, "
"routing_line_station_from_unmatched_time_penalty, and "
"routing_line_station_to_unmatched_time_penalty instead.";
cfg.routingOpts.lineUnmatchedPunishFact =
1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3;
cfg.routingOpts.lineNameFromUnmatchedPunishFact =
1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3;
cfg.routingOpts.lineNameToUnmatchedPunishFact =
1 + p.getPosDouble(secStr, "routing_line_unmatched_punish_fac") / 3;
LOG(DEBUG) << " (using routing_line_unmatched_punish_fac of "
<< cfg.routingOpts.lineUnmatchedPunishFact << " instead)";
LOG(DEBUG)
<< " (using routing_line_station_from_unmatched_time_penalty of "
<< cfg.routingOpts.lineNameFromUnmatchedPunishFact << " instead)";
LOG(DEBUG) << " (using routing_line_station_to_unmatched_time_penalty of "
<< cfg.routingOpts.lineNameToUnmatchedPunishFact
<< " instead)";
}
if (p.hasKey(secStr, "routing_line_unmatched_time_penalty_fac")) {
cfg.routingOpts.lineUnmatchedPunishFact =
p.getPosDouble(secStr, "routing_line_unmatched_time_penalty_fac");
}
if (p.hasKey(secStr, "routing_line_station_from_unmatched_time_penalty")) {
cfg.routingOpts.lineNameFromUnmatchedPunishFact = p.getPosDouble(
secStr, "routing_line_station_from_unmatched_time_penalty");
}
if (p.hasKey(secStr, "routing_line_station_to_unmatched_time_penalty")) {
cfg.routingOpts.lineNameToUnmatchedPunishFact = p.getPosDouble(
secStr, "routing_line_station_to_unmatched_time_penalty");
}
if (p.hasKey(secStr, "routing_platform_unmatched_punish")) {
procedKeys.insert("routing_platform_unmatched_punish");
curCfg.routingOpts.platformUnmatchedPen =
p.getDouble(secStr, "routing_platform_unmatched_punish");
LOG(WARN)
<< "Option routing_platform_unmatched_punish is deprecated, use "
"routing_platform_unmatched_penalty instead.";
cfg.routingOpts.platformUnmatchedPen =
p.getPosDouble(secStr, "routing_platform_unmatched_punish");
cfg.routingOpts.platformUnmatchedPen =
cfg.routingOpts.platformUnmatchedPen *
(DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]);
LOG(DEBUG) << " (using routing_platform_unmatched_penalty of "
<< cfg.routingOpts.platformUnmatchedPen << " instead)";
} else {
cfg.routingOpts.platformUnmatchedPen = 0;
}
if (p.hasKey(secStr, "routing_non_osm_station_punish")) {
procedKeys.insert("routing_non_osm_station_punish");
curCfg.routingOpts.nonOsmPen =
p.getDouble(secStr, "routing_non_osm_station_punish");
if (p.hasKey(secStr, "routing_platform_unmatched_penalty")) {
cfg.routingOpts.platformUnmatchedPen =
p.getPosDouble(secStr, "routing_platform_unmatched_penalty");
} else {
curCfg.routingOpts.nonOsmPen = 0;
// default already set above
}
if (p.hasKey(secStr, "routing_transition_penalty_fac")) {
cfg.routingOpts.transitionPen =
p.getPosDouble(secStr, "routing_transition_penalty_fac");
} else {
cfg.routingOpts.transitionPen = DEF_TRANS_PEN;
}
if (p.hasKey(secStr, "routing_station_distance_punish_fac")) {
procedKeys.insert("routing_station_distance_punish_fac");
curCfg.routingOpts.stationDistPenFactor =
p.getDouble(secStr, "routing_station_distance_punish_fac");
cfg.routingOpts.stationDistPenFactor =
p.getPosDouble(secStr, "routing_station_distance_punish_fac");
LOG(WARN) << "Option routing_station_distance_punish_fac is deprecated, "
"use routing_station_move_penalty_fac instead.";
cfg.routingOpts.stationDistPenFactor =
cfg.routingOpts.stationDistPenFactor *
(DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]);
LOG(DEBUG) << " (using routing_station_move_penalty_fac of "
<< cfg.routingOpts.stationDistPenFactor << " instead)";
} else {
curCfg.routingOpts.stationDistPenFactor = 1;
cfg.routingOpts.stationDistPenFactor =
cfg.routingOpts.stationDistPenFactor *
(DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]);
}
if (p.hasKey(secStr, "routing_station_move_penalty_fac")) {
cfg.routingOpts.stationDistPenFactor =
p.getPosDouble(secStr, "routing_station_move_penalty_fac");
} else {
// the default value was already set above
}
if (p.hasKey(secStr, "routing_non_osm_station_punish")) {
cfg.routingOpts.nonStationPen =
p.getPosDouble(secStr, "routing_non_osm_station_punish");
LOG(WARN) << "Option routing_non_osm_station_punish is deprecated, use "
"routing_non_station_penalty instead.";
cfg.routingOpts.nonStationPen =
cfg.routingOpts.nonStationPen *
(DEF_TRANS_PEN / cfg.osmBuildOpts.levelDefSpeed[0]);
LOG(DEBUG) << " (using routing_non_station_penalty of "
<< cfg.routingOpts.nonStationPen << " instead)";
} else {
cfg.routingOpts.nonStationPen = 0;
}
if (p.hasKey(secStr, "routing_non_station_penalty")) {
cfg.routingOpts.nonStationPen =
p.getPosDouble(secStr, "routing_non_station_penalty");
} else {
// default was already set above
}
if (p.hasKey(secStr, "routing_station_unmatched_penalty")) {
cfg.routingOpts.stationUnmatchedPen =
p.getPosDouble(secStr, "routing_station_unmatched_penalty");
} else {
cfg.routingOpts.stationUnmatchedPen = cfg.routingOpts.nonStationPen / 2;
}
if (p.hasKey(secStr, "station_normalize_chain")) {
procedKeys.insert("station_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
curCfg.osmBuildOpts.statNormzer =
trgraph::Normalizer(getNormRules(arr));
cfg.osmBuildOpts.statNormzer = trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "station_normalize_chain").line,
p.getVal(secStr, "station_normalize_chain").pos,
@ -369,11 +557,9 @@ void MotConfigReader::parse(const std::vector<std::string>& paths) {
}
if (p.hasKey(secStr, "track_normalize_chain")) {
procedKeys.insert("track_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
curCfg.osmBuildOpts.trackNormzer =
trgraph::Normalizer(getNormRules(arr));
cfg.osmBuildOpts.trackNormzer = trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "track_normalize_chain").line,
p.getVal(secStr, "track_normalize_chain").pos,
@ -384,11 +570,9 @@ void MotConfigReader::parse(const std::vector<std::string>& paths) {
}
if (p.hasKey(secStr, "line_normalize_chain")) {
procedKeys.insert("line_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
curCfg.osmBuildOpts.lineNormzer =
trgraph::Normalizer(getNormRules(arr));
cfg.osmBuildOpts.lineNormzer = trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "line_normalize_chain").line,
p.getVal(secStr, "line_normalize_chain").pos,
@ -399,31 +583,53 @@ void MotConfigReader::parse(const std::vector<std::string>& paths) {
}
if (p.hasKey(secStr, "station_id_normalize_chain")) {
procedKeys.insert("station_id_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "station_id_normalize_chain", ';');
curCfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr));
cfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "station_id_normalize_chain").line,
p.getVal(secStr, "station_id_normalize_chain").pos,
"<valid regular expression>",
std::string("<regex error: ") + e.what() + ">",
p.getVal(secStr, "station_normalize_chain").file);
p.getVal(secStr, "station_id_normalize_chain").file);
}
}
for (const auto& kv : p.getKeyVals(secStr)) {
if (!procedKeys.count(kv.first))
curCfg.unproced[kv.first] = kv.second.val;
// determine the maximum possible speed for this config, this is later
// used to filter out station which are so far out of reach we don't
// have to consider them for the bounding box calculation
cfg.osmBuildOpts.maxSpeed = 0;
cfg.osmBuildOpts.maxSpeedCorFac = 1;
for (size_t i = 0; i < 8; i++) {
if (cfg.osmBuildOpts.levelDefSpeed[i] > cfg.osmBuildOpts.maxSpeed)
cfg.osmBuildOpts.maxSpeed = cfg.osmBuildOpts.levelDefSpeed[i];
}
if (cfg.routingOpts.lineUnmatchedPunishFact < 1)
cfg.osmBuildOpts.maxSpeedCorFac *=
cfg.routingOpts.lineUnmatchedPunishFact;
if (cfg.routingOpts.lineNameFromUnmatchedPunishFact < 1)
cfg.osmBuildOpts.maxSpeedCorFac *=
cfg.routingOpts.lineNameFromUnmatchedPunishFact;
if (cfg.routingOpts.lineNameToUnmatchedPunishFact < 1)
cfg.osmBuildOpts.maxSpeedCorFac *=
cfg.routingOpts.lineNameToUnmatchedPunishFact;
if (cfg.routingOpts.noLinesPunishFact < 1)
cfg.osmBuildOpts.maxSpeedCorFac *= cfg.routingOpts.noLinesPunishFact;
if (cfg.osmBuildOpts.oneWaySpeedPen < 1)
cfg.osmBuildOpts.maxSpeedCorFac *= cfg.osmBuildOpts.oneWaySpeedPen;
cfg.osmBuildOpts.maxSpeed /= cfg.osmBuildOpts.maxSpeedCorFac;
bool found = false;
for (auto& cfg : _cfgs) {
if (cfg == curCfg) {
for (auto& exCfg : _cfgs) {
if (cfg == exCfg) {
for (auto mot :
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr)) {
cfg.mots.insert(mot);
exCfg.mots.insert(mot);
}
found = true;
break;
@ -431,8 +637,8 @@ void MotConfigReader::parse(const std::vector<std::string>& paths) {
}
if (!found) {
curCfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr);
_cfgs.push_back(curCfg);
cfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr);
_cfgs.push_back(cfg);
}
}
}

View file

@ -5,36 +5,13 @@
#ifndef 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 <vector>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include "ad/cppgtfs/gtfs/Route.h"
#include "configparser/ConfigFileParser.h"
#include "pfaedle/_config.h"
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/osm/OsmBuilder.h"
@ -46,7 +23,7 @@ using ad::cppgtfs::gtfs::Route;
class MotConfigReader {
public:
MotConfigReader();
void parse(const std::vector<std::string>& paths);
void parse(const std::vector<std::string>& paths, const std::string& literal);
const std::vector<MotConfig>& getConfigs() const;

View file

@ -5,11 +5,13 @@
#ifndef PFAEDLE_CONFIG_PFAEDLECONFIG_H_
#define PFAEDLE_CONFIG_PFAEDLECONFIG_H_
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <set>
#include "ad/cppgtfs/gtfs/Route.h"
#include "util/geo/Geo.h"
namespace pfaedle {
namespace config {
@ -20,39 +22,53 @@ struct Config {
Config()
: dbgOutputPath("."),
solveMethod("global"),
evalPath("."),
outputPath("gtfs-out"),
dropShapes(false),
useHMM(false),
writeGraph(false),
writeCombGraph(false),
evaluate(false),
buildTransitGraph(false),
useCaching(false),
writeOverpass(false),
writeOsmfilter(false),
inPlace(false),
gridSize(2000) {}
writeColors(false),
noFastHops(false),
noAStar(false),
noTrie(false),
noHopCache(false),
writeStats(false),
parseAdditionalGTFSFields(false),
gridSize(2000 / util::geo::M_PER_DEG),
gaussianNoise(0),
verbosity(0) {}
std::string dbgOutputPath;
std::string solveMethod;
std::string evalPath;
std::string shapeTripId;
std::string outputPath;
std::string writeOsm;
std::string osmPath;
std::string evalDfBins;
std::string motCfgParam;
std::vector<std::string> feedPaths;
std::vector<std::string> configPaths;
std::set<Route::TYPE> mots;
bool dropShapes;
bool useHMM;
bool writeGraph;
bool writeCombGraph;
bool evaluate;
bool buildTransitGraph;
bool useCaching;
bool writeOverpass;
bool writeOsmfilter;
bool inPlace;
bool writeColors;
bool noFastHops;
bool noAStar;
bool noTrie;
bool noHopCache;
bool writeStats;
bool parseAdditionalGTFSFields;
double gridSize;
double gaussianNoise;
uint8_t verbosity;
std::string toString() {
std::stringstream ss;
@ -64,10 +80,19 @@ struct Config {
<< "drop-shapes: " << dropShapes << "\n"
<< "use-hmm: " << useHMM << "\n"
<< "write-graph: " << writeGraph << "\n"
<< "write-cgraph: " << writeCombGraph << "\n"
<< "grid-size: " << gridSize << "\n"
<< "use-cache: " << useCaching << "\n"
<< "write-overpass: " << writeOverpass << "\n"
<< "write-osmfilter: " << writeOsmfilter << "\n"
<< "inplace: " << inPlace << "\n"
<< "write-colors: " << writeColors << "\n"
<< "no-fast-hops: " << noFastHops << "\n"
<< "no-a-star: " << noAStar << "\n"
<< "no-trie: " << noTrie << "\n"
<< "no-hop-cache: " << noHopCache << "\n"
<< "verbosity: " << verbosity << "\n"
<< "parse-additional-gtfs-fields: " << parseAdditionalGTFSFields << "\n"
<< "write-stats: " << writeStats << "\n"
<< "feed-paths: ";
for (const auto& p : feedPaths) {

View file

@ -1,416 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <csignal>
#include <fstream>
#include <set>
#include <string>
#include <utility>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/eval/Result.h"
#include "util/geo/Geo.h"
#include "util/geo/PolyLine.h"
#include "util/geo/output/GeoJsonOutput.h"
#include "util/log/Log.h"
using util::geo::PolyLine;
using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape;
using pfaedle::eval::Collector;
using pfaedle::eval::Result;
using util::geo::output::GeoJsonOutput;
// _____________________________________________________________________________
double Collector::add(const Trip* t, const Shape* oldS, const Shape& newS,
const std::vector<double>& newTripDists) {
if (!oldS) {
_noOrigShp++;
return 0;
}
for (auto st : t->getStopTimes()) {
if (st.getShapeDistanceTravelled() < 0) {
// we cannot safely compare trips without shape dist travelled
// info
_noOrigShp++;
return 0;
}
}
double fd = 0;
size_t unmatchedSegments;
double unmatchedSegmentsLength;
std::vector<double> oldDists;
LINE oldL = getWebMercLine(
oldS, t->getStopTimes().begin()->getShapeDistanceTravelled(),
(--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists);
std::vector<double> newDists;
LINE newL = getWebMercLine(&newS, -1, -1, &newDists);
std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json");
GeoJsonOutput gjout(fstr);
auto oldSegs = segmentize(t, oldL, oldDists, 0);
auto newSegs = segmentize(t, newL, newDists, &newTripDists);
// cut both result at the beginning and end to clear evaluation from
// loops at the end
POLYLINE oldStart = oldSegs[0];
POLYLINE newStart = newSegs[0];
auto oldStartNew =
oldStart.getSegment(oldStart.projectOn(newSegs[0][0]).totalPos, 1);
auto newStartNew =
newStart.getSegment(newStart.projectOn(oldSegs[0][0]).totalPos, 1);
if (fabs(oldStartNew.getLength() - oldStart.getLength()) /
oldStart.getLength() <
0.5 &&
fabs(newStartNew.getLength() - newStart.getLength()) /
newStart.getLength() <
0.5) {
oldSegs[0] = oldStartNew.getLine();
newSegs[0] = newStartNew.getLine();
}
POLYLINE oldEnd = oldSegs[oldSegs.size() - 1];
POLYLINE newEnd = newSegs[oldSegs.size() - 1];
auto oldEndNew =
oldEnd.getSegment(0, oldEnd.projectOn(newSegs.back().back()).totalPos);
auto newEndNew =
newEnd.getSegment(0, newEnd.projectOn(oldSegs.back().back()).totalPos);
if (fabs(oldEndNew.getLength() - oldEnd.getLength()) / oldEnd.getLength() <
0.5 &&
fabs(newEndNew.getLength() - newEnd.getLength()) / newEnd.getLength() <
0.5) {
oldSegs[oldSegs.size() - 1] = oldEndNew.getLine();
newSegs[newSegs.size() - 1] = newEndNew.getLine();
}
// check for suspicious (most likely erroneous) lines in the
// ground truth data which have a long straight-line segment
for (auto oldL : oldSegs) {
for (size_t i = 1; i < oldL.size(); i++) {
if (util::geo::webMercMeterDist(oldL[i - 1], oldL[i]) > 500) {
// return 0;
}
}
}
// new lines build from cleaned-up shapes
LINE oldLCut;
LINE newLCut;
for (auto oldL : oldSegs) {
gjout.print(oldL, util::json::Dict{{"ver", "old"}});
oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end());
}
for (auto newL : newSegs) {
gjout.print(newL, util::json::Dict{{"ver", "new"}});
newLCut.insert(newLCut.end(), newL.begin(), newL.end());
}
gjout.flush();
fstr.close();
double fac = cos(2 * atan(exp((oldSegs.front().front().getY() +
oldSegs.back().back().getY()) /
6378137.0)) -
1.5707965);
if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS.getId())) {
fd = _dCache[oldS][newS.getId()];
} else {
fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac;
_dCache[oldS][newS.getId()] = fd;
}
if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS.getId())) {
unmatchedSegments = _dACache[oldS][newS.getId()].first;
unmatchedSegmentsLength = _dACache[oldS][newS.getId()].second;
} else {
auto dA = getDa(oldSegs, newSegs);
_dACache[oldS][newS.getId()] = dA;
unmatchedSegments = dA.first;
unmatchedSegmentsLength = dA.second;
}
double totL = 0;
for (auto l : oldSegs) totL += util::geo::len(l) * fac;
// filter out shapes with a lenght of under 5 meters - they are most likely
// artifacts
if (totL < 5) {
_noOrigShp++;
return 0;
}
_fdSum += fd / totL;
_unmatchedSegSum += unmatchedSegments;
_unmatchedSegLengthSum += unmatchedSegmentsLength;
_results.insert(Result(t, fd / totL));
_resultsAN.insert(Result(t, static_cast<double>(unmatchedSegments) /
static_cast<double>(oldSegs.size())));
_resultsAL.insert(Result(t, unmatchedSegmentsLength / totL));
LOG(DEBUG) << "This result (" << t->getId()
<< "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size()
<< " = "
<< static_cast<double>(unmatchedSegments) /
static_cast<double>(oldSegs.size())
<< " A_L/L = " << unmatchedSegmentsLength << "/" << totL << " = "
<< unmatchedSegmentsLength / totL << " d_f = " << fd;
return fd;
}
// _____________________________________________________________________________
std::vector<LINE> Collector::segmentize(
const Trip* t, const LINE& shape, const std::vector<double>& dists,
const std::vector<double>* newTripDists) {
std::vector<LINE> ret;
if (t->getStopTimes().size() < 2) return ret;
POLYLINE pl(shape);
std::vector<std::pair<POINT, double> > cuts;
size_t i = 0;
for (auto st : t->getStopTimes()) {
if (newTripDists) {
cuts.push_back(std::pair<POINT, double>(
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(st.getStop()->getLat(),
st.getStop()->getLng()),
(*newTripDists)[i]));
} else {
cuts.push_back(std::pair<POINT, double>(
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(st.getStop()->getLat(),
st.getStop()->getLng()),
st.getShapeDistanceTravelled()));
}
i++;
}
// get first half of geometry, and search for start point there!
size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) -
dists.begin();
if (before + 1 > shape.size()) before = shape.size() - 1;
assert(shape.begin() + before + 1 <= shape.end());
POLYLINE l(LINE(shape.begin(), shape.begin() + before + 1));
auto lastLp = l.projectOn(cuts.front().first);
for (size_t i = 1; i < cuts.size(); i++) {
size_t before = shape.size();
if (i < cuts.size() - 1 && cuts[i + 1].second > -0.5) {
before =
std::upper_bound(dists.begin(), dists.end(), cuts[i + 1].second) -
dists.begin();
}
POLYLINE beforePl(LINE(shape.begin(), shape.begin() + before));
auto curLp = beforePl.projectOnAfter(cuts[i].first, lastLp.lastIndex);
ret.push_back(pl.getSegment(lastLp, curLp).getLine());
lastLp = curLp;
}
// std::raise(SIGABRT);
return ret;
}
// _____________________________________________________________________________
LINE Collector::getWebMercLine(const Shape* s, double from, double t) {
return getWebMercLine(s, from, t, 0);
}
// _____________________________________________________________________________
LINE Collector::getWebMercLine(const Shape* s, double from, double to,
std::vector<double>* dists) {
LINE ret;
auto i = s->getPoints().begin();
for (; i != s->getPoints().end(); i++) {
auto p = *i;
if ((from < 0 || (p.travelDist - from) > -0.01)) {
if (to >= 0 && (p.travelDist - to) > 0.01) break;
POINT mercP = util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(p.lat, p.lng);
ret.push_back(mercP);
if (dists) dists->push_back(p.travelDist);
}
}
return ret;
}
// _____________________________________________________________________________
const std::set<Result>& Collector::getResults() const { return _results; }
// _____________________________________________________________________________
double Collector::getAvgDist() const { return _fdSum / _results.size(); }
// _____________________________________________________________________________
void Collector::printHisto(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const {
size_t W = 60;
auto it = result.begin();
std::vector<std::pair<double, size_t> > res;
std::vector<const Trip*> examples;
size_t maxC = 0;
for (size_t i = 0; i < bins.size(); i++) {
size_t c = 0;
const Trip* trip = 0;
while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) {
if (!trip) trip = it->getTrip();
c++;
it++;
}
if (c > maxC) maxC = c;
examples.push_back(trip);
res.push_back(std::pair<double, size_t>(bins[i], c));
}
size_t j = 0;
for (auto r : res) {
std::string range = util::toString(r.first);
(*os) << " < " << std::setfill(' ') << std::setw(10) << range << ": ";
size_t i = 0;
for (; i < W * (static_cast<double>(r.second) / static_cast<double>(maxC));
i++) {
(*os) << "|";
}
if (r.second)
(*os) << " (" << r.second << ", e.g. #" << examples[j]->getId() << ")";
(*os) << std::endl;
j++;
}
}
// _____________________________________________________________________________
std::vector<double> Collector::getBins(double mind, double maxd, size_t steps) {
double bin = (maxd - mind) / steps;
double curE = mind + bin;
std::vector<double> ret;
while (curE <= maxd) {
ret.push_back(curE);
curE += bin;
}
return ret;
}
// _____________________________________________________________________________
void Collector::printCsv(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const {
auto it = result.begin();
std::vector<std::pair<double, size_t> > res;
for (size_t i = 0; i < bins.size(); i++) {
size_t c = 0;
const Trip* trip = 0;
while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) {
if (!trip) trip = it->getTrip();
c++;
it++;
}
res.push_back(std::pair<double, size_t>(bins[i], c));
}
(*os) << "range, count\n";
for (auto r : res) {
(*os) << r.first << "," << r.second << "\n";
}
}
// _____________________________________________________________________________
void Collector::printStats(std::ostream* os) const {
size_t buckets = 10;
(*os) << "\n ===== Evalution results =====\n\n";
(*os) << std::setfill(' ') << std::setw(30)
<< " # of trips new shapes were matched for: " << _results.size()
<< "\n";
(*os) << std::setw(30) << " # of trips without input shapes: " << _noOrigShp
<< "\n";
if (_results.size()) {
(*os) << std::setw(30) << " highest distance to input shapes: "
<< (--_results.end())->getDist() << " (on trip #"
<< (--_results.end())->getTrip()->getId() << ")\n";
(*os) << std::setw(30) << " lowest distance to input shapes: "
<< (_results.begin())->getDist() << " (on trip #"
<< (_results.begin())->getTrip()->getId() << ")\n";
(*os) << std::setw(30) << " avg total frechet distance: " << getAvgDist()
<< "\n";
std::vector<double> dfBins = getBins(
(_results.begin())->getDist(), (--_results.end())->getDist(), buckets);
if (_dfBins.size()) dfBins = _dfBins;
(*os) << "\n -- Histogram of d_f for this run -- " << std::endl;
printHisto(os, _results, dfBins);
std::ofstream fstr1(_evalOutPath + "/eval-frechet.csv");
printCsv(&fstr1, _results, dfBins);
(*os) << "\n\n\n -- Histogram of A_N/N for this run -- " << std::endl;
printHisto(os, _resultsAN,
getBins((_resultsAN.begin())->getDist(),
(--_resultsAN.end())->getDist(), buckets));
std::ofstream fstr2(_evalOutPath + "/eval-AN.csv");
printCsv(&fstr2, _resultsAN, getBins(0, 1, 20));
(*os) << "\n\n\n -- Histogram of A_L/L for this run -- " << std::endl;
printHisto(os, _resultsAL,
getBins((_resultsAL.begin())->getDist(),
(--_resultsAL.end())->getDist(), buckets));
std::ofstream fstr3(_evalOutPath + "/eval-AL.csv");
printCsv(&fstr3, _resultsAL, getBins(0, 1, 20));
}
(*os) << "\n ===== End of evaluation results =====\n";
(*os) << std::endl;
}
// _____________________________________________________________________________
std::pair<size_t, double> Collector::getDa(const std::vector<LINE>& a,
const std::vector<LINE>& b) {
assert(a.size() == b.size());
std::pair<size_t, double> ret{0, 0};
// euclidean distance on web mercator is in meters on equator,
// and proportional to cos(lat) in both y directions
double fac =
cos(2 * atan(exp((a.front().front().getY() + a.back().back().getY()) /
6378137.0)) -
1.5707965);
for (size_t i = 0; i < a.size(); i++) {
double fd = util::geo::frechetDist(a[i], b[i], 3 / fac) * fac;
if (fd >= 20) {
ret.first++;
ret.second += util::geo::len(a[i]) * fac;
}
}
return ret;
}

View file

@ -1,95 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_EVAL_COLLECTOR_H_
#define PFAEDLE_EVAL_COLLECTOR_H_
#include <map>
#include <ostream>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "pfaedle/eval/Result.h"
#include "util/geo/Geo.h"
using pfaedle::gtfs::Trip;
using ad::cppgtfs::gtfs::Shape;
namespace pfaedle {
namespace eval {
/*
* Collects routing results for evaluation
*/
class Collector {
public:
Collector(const std::string& evalOutPath, const std::vector<double>& dfBins)
: _noOrigShp(0),
_fdSum(0),
_unmatchedSegSum(0),
_unmatchedSegLengthSum(0),
_evalOutPath(evalOutPath),
_dfBins(dfBins) {}
// Add a shape found by our tool newS for a trip t with newly calculated
// station dist values with the old shape oldS
double add(const Trip* t, const Shape* oldS, const Shape& newS,
const std::vector<double>& newDists);
// Return the set of all Result objects
const std::set<Result>& getResults() const;
// Print general stats to os
void printStats(std::ostream* os) const;
// Print histogramgs for the results to os
void printHisto(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const;
// Print a CSV for the results to os
void printCsv(std::ostream* os, const std::set<Result>& result,
const std::vector<double>& bins) const;
// Return the averaged average frechet distance
double getAvgDist() const;
static LINE getWebMercLine(const Shape* s, double from, double to);
static LINE getWebMercLine(const Shape* s, double from, double to,
std::vector<double>* dists);
private:
std::set<Result> _results;
std::set<Result> _resultsAN;
std::set<Result> _resultsAL;
std::map<const Shape*, std::map<std::string, double> > _dCache;
std::map<const Shape*, std::map<std::string, std::pair<size_t, double> > >
_dACache;
size_t _noOrigShp;
double _fdSum;
size_t _unmatchedSegSum;
double _unmatchedSegLengthSum;
std::string _evalOutPath;
std::vector<double> _dfBins;
static std::pair<size_t, double> getDa(const std::vector<LINE>& a,
const std::vector<LINE>& b);
static std::vector<LINE> segmentize(const Trip* t, const LINE& shape,
const std::vector<double>& dists,
const std::vector<double>* newTripDists);
static std::vector<double> getBins(double mind, double maxd, size_t steps);
};
} // namespace eval
} // namespace pfaedle
#endif // PFAEDLE_EVAL_COLLECTOR_H_

View file

@ -6,7 +6,7 @@
#define PFAEDLE_GTFS_FEED_H_
#include <string>
#include "Route.h"
#include "Service.h"
#include "ShapeContainer.h"
#include "StopTime.h"
@ -21,14 +21,17 @@ namespace pfaedle {
namespace gtfs {
typedef ad::cppgtfs::gtfs::FeedB<
ad::cppgtfs::gtfs::Agency, Route, ad::cppgtfs::gtfs::Stop, Service,
StopTime, Shape, ad::cppgtfs::gtfs::Fare, ad::cppgtfs::gtfs::Container,
ad::cppgtfs::gtfs::ContContainer, ad::cppgtfs::gtfs::NullContainer,
ad::cppgtfs::gtfs::ContContainer, ad::cppgtfs::gtfs::ContContainer,
ShapeContainer, ad::cppgtfs::gtfs::NullContainer>
ad::cppgtfs::gtfs::Agency, ad::cppgtfs::gtfs::Route,
ad::cppgtfs::gtfs::Stop, Service, StopTime, Shape, ad::cppgtfs::gtfs::Fare,
ad::cppgtfs::gtfs::Level, ad::cppgtfs::gtfs::Pathway,
ad::cppgtfs::gtfs::Container, ad::cppgtfs::gtfs::Container,
ad::cppgtfs::gtfs::NullContainer, ad::cppgtfs::gtfs::ContContainer,
ad::cppgtfs::gtfs::ContContainer, ShapeContainer,
ad::cppgtfs::gtfs::Container, ad::cppgtfs::gtfs::Container,
ad::cppgtfs::gtfs::Container>
Feed;
typedef ad::cppgtfs::gtfs::TripB<StopTime<ad::cppgtfs::gtfs::Stop>, Service,
Route, Shape>
ad::cppgtfs::gtfs::Route, Shape>
Trip;
} // namespace gtfs

View file

@ -1,61 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_GTFS_ROUTE_H_
#define PFAEDLE_GTFS_ROUTE_H_
#include <stdint.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <sstream>
#include <string>
#include "ad/cppgtfs/gtfs/Agency.h"
#include "ad/cppgtfs/gtfs/Route.h"
#include "util/Misc.h"
using std::exception;
using std::string;
namespace pfaedle {
namespace gtfs {
class Route {
public:
typedef Route* Ref;
static std::string getId(Ref r) { return r->getId(); }
Route() {}
Route(const string& id, ad::cppgtfs::gtfs::Agency* agency,
const string& short_name, const string& long_name, const string& desc,
ad::cppgtfs::gtfs::flat::Route::TYPE type, const string& url,
uint32_t color, uint32_t text_color)
: _id(id), _short_name(short_name), _long_name(long_name), _type(type) {
UNUSED(agency);
UNUSED(desc);
UNUSED(url);
UNUSED(color);
UNUSED(text_color);
}
const std::string& getId() const { return _id; }
const std::string& getShortName() const { return _short_name; }
const std::string& getLongName() const { return _long_name; }
ad::cppgtfs::gtfs::flat::Route::TYPE getType() const { return _type; }
private:
string _id;
string _short_name;
string _long_name;
ad::cppgtfs::gtfs::flat::Route::TYPE _type;
};
} // namespace gtfs
} // namespace pfaedle
#endif // PFAEDLE_GTFS_ROUTE_H_

View file

@ -5,6 +5,7 @@
#ifndef PFAEDLE_GTFS_SHAPECONTAINER_H_
#define PFAEDLE_GTFS_SHAPECONTAINER_H_
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <set>
@ -12,6 +13,7 @@
#include <string>
#include "ad/cppgtfs/gtfs/Shape.h"
#include "ad/cppgtfs/gtfs/flat/Shape.h"
#include "pfaedle/Def.h"
#include "util/Misc.h"
namespace pfaedle {
@ -59,6 +61,7 @@ class ShapeContainer {
size_t _max;
std::string _curId;
std::stringstream _writeBuffer;
std::fpos<std::mbstate_t> _lastBuff;
};
#include "ShapeContainer.tpp"

View file

@ -2,21 +2,13 @@
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <unistd.h>
#include <iostream>
#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();
}
ShapeContainer<T>::ShapeContainer() : _lastBuff(0) {
std::string f = util::getTmpFName("<tmp>", ".pfaedle-tmp", "");
_storage.open(f, std::fstream::in | std::fstream::out | std::fstream::trunc);
// immediately unlink
@ -31,6 +23,8 @@ ShapeContainer<T>::ShapeContainer() {
// ____________________________________________________________________________
template <typename T>
ShapeContainer<T>::~ShapeContainer() {
_storage << _writeBuffer.rdbuf();
_storage.flush();
_storage.close();
}
@ -51,15 +45,15 @@ bool ShapeContainer<T>::remove(const std::string& id) {
// ____________________________________________________________________________
template <typename T>
T* ShapeContainer<T>::get(const std::string& id) {
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
UNUSED(id);
return reinterpret_cast<T*>(0);
}
// ____________________________________________________________________________
template <typename T>
const T* ShapeContainer<T>::get(const std::string& id) const {
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
UNUSED(id);
return reinterpret_cast<T*>(0);
}
// ____________________________________________________________________________
@ -78,17 +72,22 @@ size_t ShapeContainer<T>::size() const {
template <typename T>
std::string ShapeContainer<T>::add(const ad::cppgtfs::gtfs::Shape& s) {
if (has(s.getId())) return s.getId();
size_t size = s.getPoints().size();
_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';
_writeBuffer << s.getId();
_writeBuffer.put(' ');
_writeBuffer.write(reinterpret_cast<const char*>(&size), sizeof(size));
if (_writeBuffer.tellp() > 1000 * 5000) {
for (const auto& p : s.getPoints()) {
_writeBuffer.write(reinterpret_cast<const char*>(&p.lat), sizeof(p.lat));
_writeBuffer.write(reinterpret_cast<const char*>(&p.lng), sizeof(p.lng));
_writeBuffer.write(reinterpret_cast<const char*>(&p.travelDist),
sizeof(p.travelDist));
}
if (_writeBuffer.tellp() - _lastBuff > 1000 * 5000) {
_lastBuff = _writeBuffer.tellp();
_storage << _writeBuffer.rdbuf();
_writeBuffer.clear();
}
@ -100,6 +99,7 @@ std::string ShapeContainer<T>::add(const ad::cppgtfs::gtfs::Shape& s) {
template <typename T>
void ShapeContainer<T>::open() {
_storage << _writeBuffer.rdbuf();
_storage.flush();
_writeBuffer.clear();
_ptr = 0;
@ -115,14 +115,18 @@ bool ShapeContainer<T>::nextStoragePt(
while (_storage.good() && !_storage.fail()) {
if (!_ptr) {
_storage >> _curId;
_storage >> _max;
_storage.ignore();
_storage.read(reinterpret_cast<char*>(&_max), sizeof(_max));
}
if (!_storage.good() || _storage.fail()) return false;
_storage >> ret->lat;
_storage >> ret->lng;
_storage >> ret->travelDist;
_storage.read(reinterpret_cast<char*>(&ret->lat), sizeof(ret->lat));
_storage.read(reinterpret_cast<char*>(&ret->lng), sizeof(ret->lng));
_storage.read(reinterpret_cast<char*>(&ret->travelDist),
sizeof(ret->travelDist));
ret->seq = _ptr + 1;
ret->id = _curId;

View file

@ -26,15 +26,15 @@ class StopTime {
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);
bool isTp, uint8_t continuousDropOff,
uint8_t continuousPickup)
: _s(s), _sequence(seq), _dist(distTrav), _at(at), _dt(dt), _isTp(isTp) {
UNUSED(hs);
UNUSED(put);
UNUSED(dot);
UNUSED(distTrav);
UNUSED(isTp);
UNUSED(continuousDropOff);
UNUSED(continuousPickup);
}
const typename StopT::Ref getStop() const { return _s; }
@ -42,20 +42,23 @@ class StopTime {
void setShapeDistanceTravelled(double d) { _dist = d; }
ad::cppgtfs::gtfs::Time getArrivalTime() const {
return ad::cppgtfs::gtfs::Time(0, 0, 0);
return _at;
}
ad::cppgtfs::gtfs::Time getDepartureTime() const {
return ad::cppgtfs::gtfs::Time(0, 0, 0);
return _dt;
}
float getShapeDistanceTravelled() const { return _dist; }
uint16_t getSeq() const { return _sequence; }
bool isTp() const { return _isTp; }
private:
typename StopT::Ref _s;
uint32_t _sequence;
float _dist;
ad::cppgtfs::gtfs::Time _at, _dt;
bool _isTp;
};
template <typename StopTimeT>

View file

@ -2,415 +2,622 @@
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <stdio.h>
#ifdef LIBZIP_FOUND
#include <zip.h>
#endif
#include <cstdio>
#include <fstream>
#include <map>
#include <memory>
#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 ad::util::CsvWriter;
#ifdef LIBZIP_FOUND
using ad::util::ZipCsvParser;
#endif
using pfaedle::gtfs::Writer;
using util::getTmpFName;
// ____________________________________________________________________________
bool Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const {
void Writer::write(gtfs::Feed* sourceFeed, const std::string& path) const {
bool toZip =
(path.size() > 3 && 0 == path.compare(path.size() - 4, 4, ".zip"));
std::ofstream fs;
std::ifstream is;
std::string gtfsPath(path);
std::string curFile;
std::string curFileTg;
curFile = getTmpFName("agency.txt");
std::string tmpZip;
std::string zipFileName;
if (gtfsPath.size() == 0) gtfsPath = ".";
#ifdef LIBZIP_FOUND
zip* za = 0;
if (toZip) {
const size_t slashIdx = path.rfind('/');
if (slashIdx != std::string::npos) {
zipFileName = path.substr(slashIdx + 1, -1);
gtfsPath = path.substr(0, slashIdx);
} else {
zipFileName = path;
gtfsPath = ".";
}
tmpZip = getTmpFName(gtfsPath, ".pfaedle-tmp", zipFileName);
int zipErr = 0;
za = zip_open(tmpZip.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &zipErr);
if (zipErr != 0) {
char errBuf[100];
zip_error_to_str(errBuf, sizeof(errBuf), zipErr, errno);
cannotWrite(tmpZip, gtfsPath + "/" + zipFileName);
std::stringstream ss;
ss << "(temporary file for " << (gtfsPath + "/" + zipFileName)
<< ") Could not open ZIP file, reason was: " << errBuf;
throw ad::cppgtfs::WriterException(ss.str(), tmpZip);
}
#else
if (toZip) {
throw ad::cppgtfs::WriterException(
"Could not output ZIP file, pfaedle was compiled without libzip", path);
#endif
} else {
mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
}
try {
Parser ip(sourceFeed->getPath());
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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 = getTmpFName("stops.txt");
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "agency.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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 = getTmpFName("routes.txt");
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "stops.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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 = getTmpFName("calendar.txt");
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "routes.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
auto csvp = ip.getCsvParser("calendar.txt");
if (csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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());
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "calendar.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
is.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str());
if (is.good()) {
is.close();
curFile = getTmpFName("calendar_dates.txt");
csvp = ip.getCsvParser("calendar_dates.txt");
if (csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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());
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "calendar_dates.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
is.open((sourceFeed->getPath() + "/transfers.txt").c_str());
if (is.good()) {
is.close();
curFile = getTmpFName("transfers.txt");
csvp = ip.getCsvParser("transfers.txt");
if (csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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());
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "transfers.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
is.open((sourceFeed->getPath() + "/fare_attributes.txt").c_str());
if (is.good()) {
is.close();
curFile = getTmpFName("fare_attributes.txt");
csvp = ip.getCsvParser("fare_attributes.txt");
if (csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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());
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "fare_attributes.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
is.open((sourceFeed->getPath() + "/fare_rules.txt").c_str());
if (is.good()) {
is.close();
curFile = getTmpFName("fare_rules.txt");
csvp = ip.getCsvParser("fare_rules.txt");
if (csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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());
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "fare_rules.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
is.close();
curFile = getTmpFName("shapes.txt");
csvp = ip.getCsvParser("pathways.txt");
if (csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "pathways.txt");
curFileTg = gtfsPath + "/pathways.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writePathways(sourceFeed, &fs);
fs.close();
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "pathways.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
csvp = ip.getCsvParser("levels.txt");
if (csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "levels.txt");
curFileTg = gtfsPath + "/levels.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeLevels(sourceFeed, &fs);
fs.close();
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "levels.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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 = getTmpFName("trips.txt");
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "shapes.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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 = getTmpFName("frequencies.txt");
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "trips.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
csvp = ip.getCsvParser("frequencies.txt");
if (hasFreqs && csvp->isGood()) {
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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());
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "frequencies.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
is.close();
curFile = getTmpFName("stop_times.txt");
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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 (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "stop_times.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
if (!sourceFeed->getPublisherUrl().empty() &&
!sourceFeed->getPublisherName().empty()) {
curFile = getTmpFName("feed_info.txt");
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "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());
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "feed_info.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
}
return true;
curFile = getTmpFName(gtfsPath, ".pfaedle-tmp", "attributions.txt");
curFileTg = gtfsPath + "/attributions.txt";
fs.open(curFile.c_str());
if (!fs.good()) cannotWrite(curFile, curFileTg);
writeAttribution(sourceFeed, &fs);
fs.close();
if (toZip) {
#ifdef LIBZIP_FOUND
moveIntoZip(za, curFile, "attributions.txt");
#endif
} else {
if (std::rename(curFile.c_str(), curFileTg.c_str()))
cannotWrite(curFileTg);
}
} catch (...) {
#ifdef LIBZIP_FOUND
zip_discard(za);
#endif
throw;
}
if (toZip) {
#ifdef LIBZIP_FOUND
std::string targetZipPath = gtfsPath + "/" + zipFileName;
if (!za) cannotWrite(targetZipPath);
zip_close(za);
if (std::rename(tmpZip.c_str(), targetZipPath.c_str()))
cannotWrite(targetZipPath);
#endif
}
}
// ____________________________________________________________________________
bool Writer::writeFeedInfo(gtfs::Feed* f, std::ostream* os) const {
void Writer::writeAttribution(gtfs::Feed*, std::ostream* os) const {
auto csvw = ad::cppgtfs::Writer::getAttributionCsvw(os);
csvw->flushLine();
csvw->writeString("OpenStreetMap contributors");
csvw->writeString("https://www.openstreetmap.org/copyright");
csvw->writeInt(1);
csvw->flushLine();
}
// ____________________________________________________________________________
void 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());
csvw->flushLine();
csvw->writeString(f->getPublisherName());
csvw->writeString(f->getPublisherUrl());
csvw->writeString(f->getLang());
if (!f->getStartDate().empty())
csvw.writeInt(f->getStartDate().getYYYYMMDD());
csvw->writeInt(f->getStartDate().getYYYYMMDD());
else
csvw.skip();
csvw->skip();
if (!f->getEndDate().empty())
csvw.writeInt(f->getEndDate().getYYYYMMDD());
csvw->writeInt(f->getEndDate().getYYYYMMDD());
else
csvw.skip();
csvw.writeString(f->getVersion());
csvw.flushLine();
return true;
csvw->skip();
csvw->writeString(f->getVersion());
csvw->writeString(f->getContactEmail());
csvw->writeString(f->getContactUrl());
csvw->writeString(f->getDefaultLang());
csvw->flushLine();
}
// ____________________________________________________________________________
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;
void Writer::writePathways(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("pathways.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getAgencyCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getPathwayCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Pathway fa;
auto flds = Parser::getPathwayFlds(csvp.get());
while (p.nextPathway(csvp.get(), &fa, flds)) {
w.writePathway(fa, csvw.get());
}
}
// ____________________________________________________________________________
void Writer::writeLevels(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("levels.txt");
ad::cppgtfs::Writer w;
auto csvw = ad::cppgtfs::Writer::getLevelCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Level fa;
auto flds = Parser::getLevelFlds(csvp.get());
while (p.nextLevel(csvp.get(), &fa, flds)) {
w.writeLevel(fa, csvw.get());
}
}
// ____________________________________________________________________________
void Writer::writeAgency(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("agency.txt");
ad::cppgtfs::Writer w;
auto csvw =
ad::cppgtfs::Writer::getAgencyCsvw(os, sourceFeed->getAgencyAddFlds());
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Agency fa;
auto flds = Parser::getAgencyFlds(&csvp);
auto flds = Parser::getAgencyFlds(csvp.get());
while (p.nextAgency(&csvp, &fa, flds)) {
w.writeAgency(fa, &csvw);
while (p.nextAgency(csvp.get(), &fa, flds)) {
w.writeAgency(fa, csvw.get(), sourceFeed->getAgencyAddFlds());
}
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;
void Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("stops.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getStopsCsvw(os);
csvw.flushLine();
auto csvw =
ad::cppgtfs::Writer::getStopsCsvw(os, sourceFeed->getStopAddFlds());
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Stop s;
auto flds = Parser::getStopFlds(&csvp);
auto flds = Parser::getStopFlds(csvp.get());
while (p.nextStop(&csvp, &s, flds)) {
w.writeStop(s, &csvw);
while (p.nextStop(csvp.get(), &s, flds)) {
w.writeStop(s, csvw.get(), sourceFeed->getStopAddFlds());
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeRoutes(gtfs::Feed* sourceFeed, std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/routes.txt").c_str());
CsvParser csvp(&fs);
Parser p;
void Writer::writeRoutes(gtfs::Feed* sourceFeed, std::ostream* os) const {
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getRoutesCsvw(os);
csvw.flushLine();
auto csvw =
ad::cppgtfs::Writer::getRoutesCsvw(os, sourceFeed->getRouteAddFlds());
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Route s;
auto flds = Parser::getRouteFlds(&csvp);
while (p.nextRoute(&csvp, &s, flds)) {
w.writeRoute(s, &csvw);
for (auto r : sourceFeed->getRoutes()) {
w.writeRoute(r.second->getFlat(), csvw.get(),
sourceFeed->getRouteAddFlds());
}
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;
void Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("calendar.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getCalendarCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getCalendarCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Calendar c;
auto flds = Parser::getCalendarFlds(&csvp);
auto flds = Parser::getCalendarFlds(csvp.get());
while (p.nextCalendar(&csvp, &c, flds)) {
w.writeCalendar(c, &csvw);
while (p.nextCalendar(csvp.get(), &c, flds)) {
w.writeCalendar(c, csvw.get());
}
fs.close();
return true;
}
// ____________________________________________________________________________
bool Writer::writeCalendarDates(gtfs::Feed* sourceFeed,
void Writer::writeCalendarDates(gtfs::Feed* sourceFeed,
std::ostream* os) const {
std::ifstream fs;
fs.open((sourceFeed->getPath() + "/calendar_dates.txt").c_str());
CsvParser csvp(&fs);
Parser p;
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("calendar_dates.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getCalendarDatesCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getCalendarDatesCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::CalendarDate c;
auto flds = Parser::getCalendarDateFlds(&csvp);
auto flds = Parser::getCalendarDateFlds(csvp.get());
while (p.nextCalendarDate(&csvp, &c, flds)) {
w.writeCalendarDate(c, &csvw);
while (p.nextCalendarDate(csvp.get(), &c, flds)) {
w.writeCalendarDate(c, csvw.get());
}
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;
void Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("frequencies.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFrequencyCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getFrequencyCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Frequency f;
auto flds = Parser::getFrequencyFlds(&csvp);
auto flds = Parser::getFrequencyFlds(csvp.get());
while (p.nextFrequency(&csvp, &f, flds)) {
w.writeFrequency(f, &csvw);
while (p.nextFrequency(csvp.get(), &f, flds)) {
w.writeFrequency(f, csvw.get());
}
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;
void Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("transfers.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getTransfersCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getTransfersCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Transfer t;
auto flds = Parser::getTransfersFlds(&csvp);
auto flds = Parser::getTransfersFlds(csvp.get());
while (p.nextTransfer(&csvp, &t, flds)) {
w.writeTransfer(t, &csvw);
while (p.nextTransfer(csvp.get(), &t, flds)) {
w.writeTransfer(t, csvw.get());
}
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;
void Writer::writeFares(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("fare_attributes.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFaresCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getFaresCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::Fare f;
auto flds = Parser::getFareFlds(&csvp);
auto flds = Parser::getFareFlds(csvp.get());
while (p.nextFare(&csvp, &f, flds)) {
w.writeFare(f, &csvw);
while (p.nextFare(csvp.get(), &f, flds)) {
w.writeFare(f, csvw.get());
}
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;
void Writer::writeFareRules(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("fare_rules.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getFareRulesCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getFareRulesCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::FareRule f;
auto flds = Parser::getFareRuleFlds(&csvp);
auto flds = Parser::getFareRuleFlds(csvp.get());
while (p.nextFareRule(&csvp, &f, flds)) {
w.writeFareRule(f, &csvw);
while (p.nextFareRule(csvp.get(), &f, flds)) {
w.writeFareRule(f, csvw.get());
}
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();
void Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const {
auto 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;
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("shapes.txt");
auto flds = Parser::getShapeFlds(&csvp);
if (csvp->isGood()) {
auto flds = Parser::getShapeFlds(csvp.get());
std::string curShapeId;
std::string curSkipShapeId;
while (p.nextShapePoint(&csvp, &sp, flds)) {
while (p.nextShapePoint(csvp.get(), &sp, flds)) {
if (sp.id == curSkipShapeId) continue;
if (sp.id != curShapeId) {
if (sourceFeed->getShapes().has(sp.id)) {
@ -421,18 +628,14 @@ bool Writer::writeShapes(gtfs::Feed* sourceFeed, std::ostream* os) const {
}
}
w.writeShapePoint(sp, &csvw);
w.writeShapePoint(sp, csvw.get());
}
}
sourceFeed->getShapes().open();
while (sourceFeed->getShapes().nextStoragePt(&sp)) {
w.writeShapePoint(sp, &csvw);
w.writeShapePoint(sp, csvw.get());
}
fs.close();
return true;
}
// ____________________________________________________________________________
@ -440,36 +643,34 @@ 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();
auto csvw =
ad::cppgtfs::Writer::getTripsCsvw(os, sourceFeed->getTripAddFlds());
csvw->flushLine();
for (auto t : sourceFeed->getTrips()) {
if (t.getFrequencies().size()) hasFreqs = true;
w.writeTrip(t.getFlat(), &csvw);
w.writeTrip(t.getFlat(), csvw.get(), sourceFeed->getTripAddFlds());
}
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;
void Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const {
Parser p(sourceFeed->getPath());
auto csvp = p.getCsvParser("stop_times.txt");
ad::cppgtfs::Writer w;
CsvWriter csvw = ad::cppgtfs::Writer::getStopTimesCsvw(os);
csvw.flushLine();
auto csvw = ad::cppgtfs::Writer::getStopTimesCsvw(os);
csvw->flushLine();
ad::cppgtfs::gtfs::flat::StopTime st;
auto flds = Parser::getStopTimeFlds(&csvp);
auto flds = Parser::getStopTimeFlds(csvp.get());
std::string curTripId;
Trip* cur = 0;
while (p.nextStopTime(&csvp, &st, flds)) {
while (p.nextStopTime(csvp.get(), &st, flds)) {
// we may have changed to distance field
if (curTripId != st.trip) {
cur = sourceFeed->getTrips().get(st.trip);
@ -480,11 +681,15 @@ bool Writer::writeStopTimes(gtfs::Feed* sourceFeed, std::ostream* os) const {
st.shapeDistTravelled = stN.getShapeDistanceTravelled();
}
w.writeStopTime(st, &csvw);
w.writeStopTime(st, csvw.get());
}
}
fs.close();
return true;
// ___________________________________________________________________________
void Writer::cannotWrite(const std::string& file) {
std::stringstream ss;
ss << "Could not write to file";
throw ad::cppgtfs::WriterException(ss.str(), file);
}
// ___________________________________________________________________________
@ -494,17 +699,25 @@ void Writer::cannotWrite(const std::string& file, const std::string& file2) {
throw ad::cppgtfs::WriterException(ss.str(), file);
}
// _____________________________________________________________________________
std::string Writer::getTmpFName(const std::string& postf) {
std::string f = ".pfaedle-tmp-" + postf;
while (access(f.c_str(), F_OK) != -1) {
// ___________________________________________________________________________
#ifdef LIBZIP_FOUND
void Writer::moveIntoZip(zip* za, const std::string& sourcePath,
const std::string& targetPath) {
zip_source_t* s;
FILE* fp = fopen(sourcePath.c_str(), "r");
if (fp == 0) {
std::stringstream ss;
ss << ".pfaedle-tmp-";
ss << postf << "-";
ss << std::rand();
f = ss.str().c_str();
ss << "(temporary file for " << targetPath << ") Could not open file";
throw ad::cppgtfs::WriterException(ss.str(), sourcePath);
}
return f;
// immediately unlink
unlink(sourcePath.c_str());
if ((s = zip_source_filep(za, fp, 0, -1)) == 0 ||
zip_file_add(za, targetPath.c_str(), s, ZIP_FL_ENC_UTF_8) < 0) {
zip_source_free(s);
cannotWrite(targetPath);
}
}
#endif

View file

@ -5,9 +5,15 @@
#ifndef PFAEDLE_GTFS_WRITER_H_
#define PFAEDLE_GTFS_WRITER_H_
#include <memory>
#include <string>
#include "ad/cppgtfs/Writer.h"
#ifdef LIBZIP_FOUND
#include <zip.h>
#endif
#include "Feed.h"
#include "ad/cppgtfs/Parser.h"
#include "ad/cppgtfs/Writer.h"
namespace pfaedle {
namespace gtfs {
@ -16,25 +22,35 @@ class Writer {
public:
Writer() {}
bool write(Feed* sourceFeed, const std::string& path) const;
void write(Feed* sourceFeed, const std::string& path) const;
private:
bool writeFeedInfo(Feed* f, std::ostream* os) const;
bool writeAgency(Feed* f, std::ostream* os) const;
bool writeStops(Feed* f, std::ostream* os) const;
bool writeRoutes(Feed* f, std::ostream* os) const;
bool writeCalendar(Feed* f, std::ostream* os) const;
bool writeCalendarDates(Feed* f, std::ostream* os) const;
bool writeFrequencies(Feed* f, std::ostream* os) const;
bool writeTransfers(Feed* f, std::ostream* os) const;
bool writeFares(Feed* f, std::ostream* os) const;
bool writeFareRules(Feed* f, std::ostream* os) const;
bool writeShapes(Feed* f, std::ostream* os) const;
void writeFeedInfo(Feed* f, std::ostream* os) const;
void writeAgency(Feed* f, std::ostream* os) const;
void writeStops(Feed* f, std::ostream* os) const;
void writeRoutes(Feed* f, std::ostream* os) const;
void writeCalendar(Feed* f, std::ostream* os) const;
void writeCalendarDates(Feed* f, std::ostream* os) const;
void writeFrequencies(Feed* f, std::ostream* os) const;
void writeTransfers(Feed* f, std::ostream* os) const;
void writeFares(Feed* f, std::ostream* os) const;
void writeFareRules(Feed* f, std::ostream* os) const;
void writeShapes(Feed* f, std::ostream* os) const;
bool writeTrips(Feed* f, std::ostream* os) const;
bool writeStopTimes(Feed* f, std::ostream* os) const;
void writeStopTimes(Feed* f, std::ostream* os) const;
void writeLevels(Feed* f, std::ostream* os) const;
void writePathways(Feed* f, std::ostream* os) const;
void writeAttribution(Feed* f, std::ostream* os) const;
static void cannotWrite(const std::string& file, const std::string& file2);
static std::string getTmpFName(const std::string& postf);
static void cannotWrite(const std::string& file);
#ifdef LIBZIP_FOUND
static void moveIntoZip(zip* za, const std::string& sourcePath,
const std::string& targetPath);
#endif
mutable std::ifstream _ifs;
};
} // namespace gtfs

View file

@ -26,7 +26,7 @@ namespace netgraph {
class EdgePL {
public:
EdgePL() {}
EdgePL(const LINE& l, const std::set<const Trip*>& trips)
EdgePL(const LINE& l, const std::vector<const Trip*>& trips)
: _l(l), _trips(trips) {
for (const auto t : _trips) {
_routeShortNames.insert(t->getRoute()->getShortName());
@ -46,7 +46,7 @@ class EdgePL {
private:
LINE _l;
std::set<const Trip*> _trips;
std::vector<const Trip*> _trips;
std::set<std::string> _routeShortNames;
std::set<std::string> _tripShortNames;
};

View file

@ -31,10 +31,10 @@ bool BBoxIdx::contains(const Point<double>& p) const {
// _____________________________________________________________________________
BOX BBoxIdx::getFullWebMercBox() const {
return BOX(
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(
_root.box.getLowerLeft().getY(), _root.box.getLowerLeft().getX()),
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(
_root.box.getUpperRight().getY(), _root.box.getUpperRight().getX()));
util::geo::latLngToWebMerc<PFDL_PREC>(_root.box.getLowerLeft().getY(),
_root.box.getLowerLeft().getX()),
util::geo::latLngToWebMerc<PFDL_PREC>(_root.box.getUpperRight().getY(),
_root.box.getUpperRight().getX()));
}
// _____________________________________________________________________________

View file

@ -5,10 +5,12 @@
#ifndef PFAEDLE_OSM_OSM_H_
#define PFAEDLE_OSM_OSM_H_
#include <stdint.h>
#include <set>
#include <string>
#include <utility>
#include <unordered_map>
#include <utility>
#include <vector>
namespace pfaedle {

File diff suppressed because it is too large Load diff

View file

@ -25,25 +25,24 @@
#include "util/Nullable.h"
#include "util/geo/Geo.h"
#include "util/xml/XmlWriter.h"
#include "xml/File.h"
#include "pfxml/pfxml.h"
namespace pfaedle {
namespace osm {
using ad::cppgtfs::gtfs::Stop;
using pfaedle::router::NodeSet;
using pfaedle::trgraph::Component;
using pfaedle::trgraph::Edge;
using pfaedle::trgraph::EdgeGrid;
using pfaedle::trgraph::NodeGrid;
using pfaedle::trgraph::Normalizer;
using pfaedle::trgraph::EdgePL;
using pfaedle::trgraph::Graph;
using pfaedle::trgraph::Node;
using pfaedle::trgraph::NodeGrid;
using pfaedle::trgraph::NodePL;
using pfaedle::trgraph::Edge;
using pfaedle::trgraph::EdgePL;
using pfaedle::trgraph::TransitEdgeLine;
using pfaedle::trgraph::Normalizer;
using pfaedle::trgraph::StatInfo;
using pfaedle::trgraph::StatGroup;
using pfaedle::trgraph::Component;
using pfaedle::router::NodeSet;
using ad::cppgtfs::gtfs::Stop;
using pfaedle::trgraph::TransitEdgeLine;
using util::Nullable;
struct NodeCand {
@ -58,9 +57,8 @@ struct SearchFunc {
};
struct EqSearch : public SearchFunc {
explicit EqSearch(bool orphanSnap) : orphanSnap(orphanSnap) {}
EqSearch() {}
double minSimi = 0.9;
bool orphanSnap;
bool operator()(const Node* cand, const StatInfo* si) const;
};
@ -87,14 +85,19 @@ class OsmBuilder {
// Read the OSM file at path, and write a graph to g. Only elements
// inside the bounding box will be read
void read(const std::string& path, const OsmReadOpts& opts, Graph* g,
const BBoxIdx& box, size_t gridSize, router::FeedStops* fs,
Restrictor* res);
const BBoxIdx& box, double gridSize, Restrictor* res);
// Based on the list of options, output an overpass XML query for getting
// the data needed for routing
void overpassQryWrite(std::ostream* out, const std::vector<OsmReadOpts>& opts,
const BBoxIdx& latLngBox) const;
// Based on the list of options, output an osmfilter configuration file
// to filter the data needed for routing
void osmfilterRuleWrite(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
// OSM file to out which contains exactly the entities that are needed
// from the file at in
@ -102,37 +105,37 @@ class OsmBuilder {
const std::vector<OsmReadOpts>& opts, const BBoxIdx& box);
private:
xml::ParserState readBBoxNds(xml::File* xml, OsmIdSet* nodes,
pfxml::parser_state readBBoxNds(pfxml::file* xml, OsmIdSet* nodes,
OsmIdSet* noHupNodes, const OsmFilter& filter,
const BBoxIdx& bbox) const;
void readRels(xml::File* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels,
void readRels(pfxml::file* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels,
const OsmFilter& filter, const AttrKeySet& keepAttrs,
Restrictions* rests) const;
void readRestr(const OsmRel& rel, Restrictions* rests,
const OsmFilter& filter) const;
void readNodes(xml::File* f, Graph* g, const RelLst& rels,
void readNodes(pfxml::file* f, Graph* g, const RelLst& rels,
const RelMap& nodeRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, NIdMap* nodes,
NIdMultMap* multNodes, NodeSet* orphanStations,
const AttrKeySet& keepAttrs, const FlatRels& flatRels,
const OsmReadOpts& opts) const;
void readWriteNds(xml::File* i, util::xml::XmlWriter* o,
void readWriteNds(pfxml::file* i, util::xml::XmlWriter* o,
const RelMap& nodeRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, NIdMap* nodes,
const AttrKeySet& keepAttrs, const FlatRels& f) const;
void readWriteWays(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways,
void readWriteWays(pfxml::file* i, util::xml::XmlWriter* o, OsmIdList* ways,
const AttrKeySet& keepAttrs) const;
void readWriteRels(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways,
void readWriteRels(pfxml::file* i, util::xml::XmlWriter* o, OsmIdList* ways,
NIdMap* nodes, const OsmFilter& filter,
const AttrKeySet& keepAttrs);
void readEdges(xml::File* xml, Graph* g, const RelLst& rels,
void readEdges(pfxml::file* xml, Graph* g, const RelLst& rels,
const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, NIdMap* nodes,
NIdMultMap* multNodes, const OsmIdSet& noHupNodes,
@ -140,21 +143,22 @@ class OsmBuilder {
Restrictor* restor, const FlatRels& flatRels,
EdgTracks* etracks, const OsmReadOpts& opts);
void readEdges(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
OsmIdList* ret, NIdMap* nodes, const FlatRels& flatRels);
void readEdges(pfxml::file* xml, const RelMap& wayRels,
const OsmFilter& filter, const OsmIdSet& bBoxNodes,
const AttrKeySet& keepAttrs, OsmIdList* ret, NIdMap* nodes,
const FlatRels& flatRels);
OsmWay nextWay(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
const FlatRels& flatRels) const;
OsmWay nextWay(pfxml::file* xml, const RelMap& wayRels,
const OsmFilter& filter, const OsmIdSet& bBoxNodes,
const AttrKeySet& keepAttrs, const FlatRels& flatRels) const;
bool keepWay(const OsmWay& w, const RelMap& wayRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const FlatRels& fl) const;
OsmWay nextWayWithId(xml::File* xml, osmid wid,
OsmWay nextWayWithId(pfxml::file* xml, osmid wid,
const AttrKeySet& keepAttrs) const;
OsmNode nextNode(xml::File* xml, NIdMap* nodes, NIdMultMap* multNodes,
OsmNode nextNode(pfxml::file* xml, NIdMap* nodes, NIdMultMap* multNodes,
const RelMap& nodeRels, const OsmFilter& filter,
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
const FlatRels& flatRels) const;
@ -164,56 +168,49 @@ class OsmBuilder {
const OsmIdSet& bBoxNodes, const OsmFilter& filter,
const FlatRels& fl) const;
OsmRel nextRel(xml::File* xml, const OsmFilter& filter,
OsmRel nextRel(pfxml::file* xml, const OsmFilter& filter,
const AttrKeySet& keepAttrs) const;
protected:
Nullable<StatInfo> getStatInfo(Node* node, osmid nid, const POINT& pos,
const AttrMap& m, StAttrGroups* groups,
Nullable<StatInfo> getStatInfo(osmid nid, const AttrMap& m,
const RelMap& nodeRels, const RelLst& rels,
const OsmReadOpts& ops) const;
static void snapStats(const OsmReadOpts& opts, Graph* g, const BBoxIdx& bbox,
size_t gridSize, router::FeedStops* fs, Restrictor* res,
double gridSize, Restrictor* res,
const NodeSet& orphanStations);
static void writeGeoms(Graph* g);
static void deleteOrphNds(Graph* g);
static void deleteOrphEdgs(Graph* g, const OsmReadOpts& opts);
static void writeGeoms(Graph* g, const OsmReadOpts& opts);
static void deleteOrphNds(Graph* g, const OsmReadOpts& opts);
static double dist(const Node* a, const Node* b);
static double webMercDist(const Node* a, const Node* b);
static NodeGrid buildNodeIdx(Graph* g, size_t size, const BOX& webMercBox,
static NodeGrid buildNodeIdx(Graph* g, double size, const BOX& box,
bool which);
static EdgeGrid buildEdgeIdx(Graph* g, size_t size, const BOX& webMercBox);
static EdgeGrid buildEdgeIdx(Graph* g, double size, const BOX& box);
static void fixGaps(Graph* g, NodeGrid* ng);
static void collapseEdges(Graph* g);
static void writeODirEdgs(Graph* g, Restrictor* restor);
static void writeSelfEdgs(Graph* g);
static void writeOneWayPens(Graph* g, const OsmReadOpts& opts);
static void writeNoLinePens(Graph* g, const OsmReadOpts& opts);
static void writeEdgeTracks(const EdgTracks& tracks);
static void simplifyGeoms(Graph* g);
static uint32_t writeComps(Graph* g);
static uint32_t writeComps(Graph* g, const OsmReadOpts& opts);
static bool edgesSim(const Edge* a, const Edge* b);
static const EdgePL& mergeEdgePL(Edge* a, Edge* b);
static void getEdgCands(const POINT& s, EdgeCandPQ* ret, EdgeGrid* eg,
double d);
static std::set<Node*> getMatchingNds(const NodePL& s, NodeGrid* ng,
double d);
static Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d);
static NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
static void snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
const OsmReadOpts& opts, Restrictor* restor,
bool surHeur, bool orphSnap, double maxD);
double maxD);
// Checks if from the edge e, a station similar to si can be reach with less
// than maxD distance and less or equal to "maxFullTurns" full turns. If
// such a station exists, it is returned. If not, 0 is returned.
static Node* eqStatReach(const Edge* e, const StatInfo* si, const POINT& p,
double maxD, int maxFullTurns, double maxAng,
bool orph);
double maxD, int maxFullTurns, double maxAng);
static Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p,
double maxD, int maxFullTurns, double minAngle,
@ -223,8 +220,6 @@ class OsmBuilder {
double maxD, int maxFullTurns, double minAngle);
static bool keepFullTurn(const trgraph::Node* n, double ang);
static StatGroup* groupStats(const NodeSet& s);
static NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops);
std::vector<TransitEdgeLine*> getLines(const std::vector<size_t>& edgeRels,
@ -233,7 +228,7 @@ class OsmBuilder {
void getKeptAttrKeys(const OsmReadOpts& opts, AttrKeySet sets[3]) const;
void skipUntil(xml::File* xml, const std::string& s) const;
void skipUntil(pfxml::file* xml, const std::string& s) const;
void processRestr(osmid nid, osmid wid, const Restrictions& rawRests, Edge* e,
Node* n, Restrictor* restor) const;
@ -254,6 +249,10 @@ class OsmBuilder {
bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const;
uint32_t parseHexColor(std::string) const;
static uint32_t costToInt(double c);
std::map<TransitEdgeLine, TransitEdgeLine*> _lines;
std::map<size_t, TransitEdgeLine*> _relLines;
};

View file

@ -26,6 +26,7 @@ OsmFilter::OsmFilter(const OsmReadOpts& o)
_posRestr(o.restrPosRestr),
_negRestr(o.restrNegRestr),
_noRestr(o.noRestrFilter),
_turnCycle(o.turnCycleFilter),
_levels(o.levelFilters) {}
// _____________________________________________________________________________
@ -72,6 +73,11 @@ uint64_t OsmFilter::blocker(const AttrMap& attrs) const {
return contained(attrs, _blocker, NODE);
}
// _____________________________________________________________________________
uint64_t OsmFilter::turnCycle(const AttrMap& attrs) const {
return contained(attrs, _turnCycle, NODE);
}
// _____________________________________________________________________________
uint64_t OsmFilter::contained(const AttrMap& attrs, const MultAttrMap& map,
Type t) {

View file

@ -5,8 +5,11 @@
#ifndef PFAEDLE_OSM_OSMFILTER_H_
#define PFAEDLE_OSM_OSMFILTER_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "pfaedle/osm/Osm.h"
#include "pfaedle/osm/OsmReadOpts.h"
@ -27,6 +30,7 @@ class OsmFilter {
uint64_t onewayrev(const AttrMap& attrs) const;
uint64_t station(const AttrMap& attrs) const;
uint64_t blocker(const AttrMap& attrs) const;
uint64_t turnCycle(const AttrMap& attrs) const;
uint64_t negRestr(const AttrMap& attrs) const;
uint64_t posRestr(const AttrMap& attrs) const;
std::vector<std::string> getAttrKeys() const;
@ -46,7 +50,7 @@ class OsmFilter {
private:
MultAttrMap _keep, _drop, _nohup, _oneway, _onewayrev, _twoway, _station,
_blocker, _posRestr, _negRestr, _noRestr;
_blocker, _posRestr, _negRestr, _noRestr, _turnCycle;
const MultAttrMap* _levels;
};
} // namespace osm

View file

@ -4,7 +4,6 @@
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <algorithm>
#include <cassert>
#include <climits>
@ -13,7 +12,10 @@
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
#include "pfaedle/Def.h"
#include "pfaedle/osm/OsmIdSet.h"
#include "util/3rdparty/MurmurHash3.h"
using pfaedle::osm::OsmIdSet;
@ -27,35 +29,61 @@ OsmIdSet::OsmIdSet()
_last(0),
_smallest(-1),
_biggest(0),
_hasInv(false),
_obufpos(0),
_curBlock(-1),
_fsize(0) {
_bitset = new std::bitset<BLOOMF_BITS>();
_bitsetNotIn = new std::bitset<BLOOMF_BITS>();
_file = openTmpFile();
_buffer = new unsigned char[BUFFER_S];
_outBuffer = new unsigned char[OBUFFER_S];
_outBuffer = new unsigned char[BUFFER_S];
}
// _____________________________________________________________________________
OsmIdSet::~OsmIdSet() {
delete _bitset;
delete _bitsetNotIn;
delete[] _buffer;
if (!_closed) delete[] _outBuffer;
}
// _____________________________________________________________________________
void OsmIdSet::nadd(osmid id) {
if (_closed) throw std::exception();
_hasInv = true;
uint32_t h1, h2;
MurmurHash3_x86_32(&id, 8, 469954432, &h1);
h2 = jenkins(id);
for (int i = 0; i < 5; i++) {
uint32_t h = (h1 + i * h2) % BLOOMF_BITS;
(*_bitsetNotIn)[h] = 1;
}
}
// _____________________________________________________________________________
void OsmIdSet::add(osmid id) {
if (_closed) throw std::exception();
diskAdd(id);
// _set.insert(id);
if (_last > id) _sorted = false;
_last = id;
if (id < _smallest) _smallest = id;
if (id > _biggest) _biggest = id;
for (int i = 0; i < 10; i++) (*_bitset)[hash(id, i)] = 1;
uint32_t h1, h2;
MurmurHash3_x86_32(&id, 8, 469954432, &h1);
h2 = jenkins(id);
for (int i = 0; i < 5; i++) {
uint32_t h = (h1 + i * h2) % BLOOMF_BITS;
(*_bitset)[h] = 1;
}
}
// _____________________________________________________________________________
@ -69,8 +97,8 @@ void OsmIdSet::diskAdd(osmid id) {
_blockEnds.push_back(id);
}
if (_obufpos >= OBUFFER_S) {
ssize_t w = cwrite(_file, _outBuffer, OBUFFER_S);
if (_obufpos >= BUFFER_S) {
ssize_t w = cwrite(_file, _outBuffer, BUFFER_S);
_fsize += w;
_obufpos = 0;
}
@ -86,7 +114,8 @@ size_t OsmIdSet::getBlock(osmid id) const {
bool OsmIdSet::diskHas(osmid id) const {
assert(_sorted);
if (std::find(_blockEnds.begin(), _blockEnds.end(), id) != _blockEnds.end()) {
auto a = std::lower_bound(_blockEnds.begin(), _blockEnds.end(), id);
if (a != _blockEnds.end() && *a == id) {
return true;
}
@ -125,16 +154,26 @@ bool OsmIdSet::has(osmid id) const {
LOOKUPS++;
if (!_closed) close();
// trivial cases
if (id < _smallest || id > _biggest) {
return false;
}
for (int i = 0; i < 10; i++) {
if ((*_bitset)[hash(id, i)] == 0) return false;
uint32_t h1, h2;
MurmurHash3_x86_32(&id, 8, 469954432, &h1);
h2 = jenkins(id);
for (int i = 0; i < 5; i++) {
uint32_t h = (h1 + i * h2) % BLOOMF_BITS;
if ((*_bitset)[h] == 0) {
return false;
}
if (_hasInv && (*_bitsetNotIn)[h] == 0) {
return true;
}
}
bool has = diskHas(id);
// assert(has == (bool)_set.count(id));
return has;
}
@ -232,7 +271,7 @@ void OsmIdSet::sort() const {
size_t OsmIdSet::cwrite(int f, const void* buf, size_t n) const {
ssize_t w = write(f, buf, n);
if (w < 0) {
throw std::runtime_error("OSMIDSET: could not write to tmp file.\n");
throw std::runtime_error("Could not write to tmp file.\n");
}
return w;
@ -242,7 +281,7 @@ size_t OsmIdSet::cwrite(int f, const void* buf, size_t n) const {
size_t OsmIdSet::cread(int f, void* buf, size_t n) const {
ssize_t w = read(f, buf, n);
if (w < 0) {
throw std::runtime_error("OSMIDSET: could not read from tmp file.\n");
throw std::runtime_error("Could not read from tmp file.\n");
}
return w;
@ -250,8 +289,8 @@ size_t OsmIdSet::cread(int f, void* buf, size_t n) const {
// _____________________________________________________________________________
uint32_t OsmIdSet::knuth(uint32_t in) const {
const uint32_t prime = 2654435769;
return (in * prime) >> 2;
const uint32_t a = 2654435769;
return (in * a) >> 2;
}
// _____________________________________________________________________________
@ -265,14 +304,9 @@ uint32_t OsmIdSet::jenkins(uint32_t in) const {
return in >> 2;
}
// _____________________________________________________________________________
uint32_t OsmIdSet::hash(uint32_t in, int i) const {
return (knuth(in) + jenkins(in) * i) % BLOOMF_BITS;
}
// _____________________________________________________________________________
int OsmIdSet::openTmpFile() const {
const std::string& fname = getFName();
const std::string& fname = util::getTmpFName("<tmp>", ".pfaedle-tmp", "");
int file = open(fname.c_str(), O_RDWR | O_CREAT, 0666);
// immediately unlink
@ -288,17 +322,3 @@ int OsmIdSet::openTmpFile() const {
#endif
return file;
}
// _____________________________________________________________________________
std::string OsmIdSet::getFName() const {
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();
}
return f;
}

View file

@ -25,7 +25,7 @@ static const size_t BUFFER_S = 8 * 64 * 1024;
static const size_t SORT_BUFFER_S = 8 * 64 * 1024;
static const size_t OBUFFER_S = 8 * 1024 * 1024;
#define BLOOMF_BITS 400000000
#define BLOOMF_BITS 214748357
/*
* A disk-based set for OSM ids. Read-access for checking the presence is
@ -39,6 +39,9 @@ class OsmIdSet {
// Add an OSM id
void add(osmid id);
// Add an OSM id that is NOT contained
void nadd(osmid id);
// Check if an OSM id is contained
bool has(osmid id) const;
@ -47,7 +50,7 @@ class OsmIdSet {
static size_t FLOOKUPS;
private:
std::set<osmid> _set;
std::string _tmpPath;
mutable bool _closed;
mutable int _file;
unsigned char* _buffer;
@ -57,6 +60,8 @@ class OsmIdSet {
osmid _smallest;
osmid _biggest;
bool _hasInv;
size_t _obufpos;
mutable size_t _curBlock;
mutable ssize_t _curBlockSize;
@ -64,13 +69,14 @@ class OsmIdSet {
// bloom filter
std::bitset<BLOOMF_BITS>* _bitset;
std::bitset<BLOOMF_BITS>* _bitsetNotIn;
mutable std::vector<osmid> _blockEnds;
mutable size_t _fsize;
uint32_t knuth(uint32_t in) const;
uint32_t jenkins(uint32_t in) const;
uint32_t hash(uint32_t in, int i) const;
void diskAdd(osmid id);
void close() const;
void sort() const;

View file

@ -5,14 +5,14 @@
#ifndef PFAEDLE_OSM_OSMREADOPTS_H_
#define PFAEDLE_OSM_OSMREADOPTS_H_
#include <map>
#include <queue>
#include <unordered_set>
#include <set>
#include <string>
#include <unordered_map>
#include <map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <set>
#include "pfaedle/osm/Osm.h"
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/trgraph/Normalizer.h"
@ -77,11 +77,12 @@ struct RelLineRules {
AttrLst sNameRule;
AttrLst fromNameRule;
AttrLst toNameRule;
AttrLst colorRule;
};
inline bool operator==(const RelLineRules& a, const RelLineRules& b) {
return a.sNameRule == b.sNameRule && a.fromNameRule == b.fromNameRule &&
a.toNameRule == b.toNameRule;
a.toNameRule == b.toNameRule && a.colorRule == b.colorRule;
}
struct StationAttrRules {
@ -94,21 +95,6 @@ inline bool operator==(const StationAttrRules& a, const StationAttrRules& b) {
return a.nameRule == b.nameRule && a.platformRule == b.platformRule;
}
struct StatGroupNAttrRule {
DeepAttrRule attr;
double maxDist;
};
inline bool operator==(const StatGroupNAttrRule& a,
const StatGroupNAttrRule& b) {
return a.attr == b.attr && a.maxDist == b.maxDist;
}
typedef std::unordered_map<
std::string,
std::unordered_map<std::string, std::vector<trgraph::StatGroup*>>>
StAttrGroups;
struct OsmReadOpts {
OsmReadOpts() {}
@ -121,7 +107,7 @@ struct OsmReadOpts {
MultAttrMap twoWayFilter;
MultAttrMap stationFilter;
MultAttrMap stationBlockerFilter;
std::vector<StatGroupNAttrRule> statGroupNAttrRules;
MultAttrMap turnCycleFilter;
trgraph::Normalizer statNormzer;
trgraph::Normalizer lineNormzer;
@ -136,14 +122,23 @@ struct OsmReadOpts {
uint8_t maxSnapLevel;
double maxAngleSnapReach;
std::vector<double> maxSnapDistances;
double maxSnapFallbackHeurDistance;
double maxSnapDistance;
double maxStationCandDistance;
double maxBlockDistance;
double maxOsmStationDistance;
double maxSpeed;
double maxSpeedCorFac;
// TODO(patrick): this is not implemented yet
double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0};
std::vector<double> maxOsmStationDistances;
// given in km/h, but store in m/s
double levelDefSpeed[8] = {85 * 0.2777, 70 * 0.2777, 55 * 0.2777, 40 * 0.2777,
30 * 0.2777, 20 * 0.2777, 10 * 0.2777, 5 * 0.2777};
double oneWaySpeedPen;
double oneWayEntryCost;
double noLinesPunishFact;
double fullTurnAngle;
@ -154,9 +149,10 @@ struct OsmReadOpts {
};
inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
if (a.maxSnapDistances.size() != b.maxSnapDistances.size()) return false;
for (size_t i = 0; i < a.maxSnapDistances.size(); i++) {
if (fabs(a.maxSnapDistances[i] - b.maxSnapDistances[i]) >= 0.1)
if (a.maxOsmStationDistances.size() != b.maxOsmStationDistances.size())
return false;
for (size_t i = 0; i < a.maxOsmStationDistances.size(); i++) {
if (fabs(a.maxOsmStationDistances[i] - b.maxOsmStationDistances[i]) >= 0.1)
return false;
}
@ -173,24 +169,29 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
a.twoWayFilter == b.twoWayFilter &&
a.stationFilter == b.stationFilter &&
a.stationBlockerFilter == b.stationBlockerFilter &&
a.statGroupNAttrRules == b.statGroupNAttrRules &&
a.turnCycleFilter == b.turnCycleFilter &&
a.statNormzer == b.statNormzer && a.lineNormzer == b.lineNormzer &&
a.trackNormzer == b.trackNormzer && a.relLinerules == b.relLinerules &&
a.statAttrRules == b.statAttrRules &&
a.maxSnapLevel == b.maxSnapLevel &&
fabs(a.maxAngleSnapReach - b.maxAngleSnapReach) < 0.1 &&
fabs(a.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 &&
fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) <
0.1 &&
fabs(a.maxSnapDistance - b.maxSnapDistance) < 0.1 &&
fabs(a.maxStationCandDistance - b.maxStationCandDistance) < 0.1 &&
fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 &&
fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 &&
fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 &&
fabs(a.levelSnapPunishFac[2] - b.levelSnapPunishFac[2]) < 0.1 &&
fabs(a.levelSnapPunishFac[3] - b.levelSnapPunishFac[3]) < 0.1 &&
fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 &&
fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 &&
fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 &&
fabs(a.levelDefSpeed[0] - b.levelDefSpeed[0]) < 0.1 &&
fabs(a.levelDefSpeed[1] - b.levelDefSpeed[1]) < 0.1 &&
fabs(a.levelDefSpeed[2] - b.levelDefSpeed[2]) < 0.1 &&
fabs(a.levelDefSpeed[3] - b.levelDefSpeed[3]) < 0.1 &&
fabs(a.levelDefSpeed[4] - b.levelDefSpeed[4]) < 0.1 &&
fabs(a.levelDefSpeed[5] - b.levelDefSpeed[5]) < 0.1 &&
fabs(a.levelDefSpeed[6] - b.levelDefSpeed[6]) < 0.1 &&
fabs(a.levelDefSpeed[7] - b.levelDefSpeed[7]) < 0.1 &&
fabs(a.oneWaySpeedPen - b.oneWaySpeedPen) < 0.1 &&
fabs(a.oneWayEntryCost - b.oneWayEntryCost) < 0.1 &&
fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.1 &&
fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.1 &&
fabs(a.maxSpeedCorFac - b.maxSpeedCorFac) < 0.1 &&
fabs(a.maxSpeed - b.maxSpeed) < 0.1 &&
a.restrPosRestr == b.restrPosRestr &&
a.restrNegRestr == b.restrNegRestr &&
a.noRestrFilter == b.noRestrFilter;

View file

@ -16,7 +16,7 @@ namespace router {
using util::editDist;
// _____________________________________________________________________________
inline double statSimi(const std::string& a, const std::string& b) {
inline bool statSimi(const std::string& a, const std::string& b) {
if (a == b) return 1;
if (a.empty() || b.empty()) return 0;
@ -55,7 +55,7 @@ inline double statSimi(const std::string& a, const std::string& b) {
}
// _____________________________________________________________________________
inline double lineSimi(const std::string& a, const std::string& b) {
inline bool lineSimi(const std::string& a, const std::string& b) {
if (a == b) return 1;
if (a.empty() || b.empty()) return 0;

View file

@ -1,88 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include "pfaedle/Def.h"
#include "util/geo/Geo.h"
#include "pfaedle/router/EdgePL.h"
#include "pfaedle/router/Router.h"
#include "util/String.h"
using pfaedle::router::EdgePL;
using pfaedle::router::EdgeCost;
using pfaedle::router::EdgeList;
using pfaedle::trgraph::Node;
// _____________________________________________________________________________
EdgeList* EdgePL::getEdges() { return &_edges; }
// _____________________________________________________________________________
const EdgeList& EdgePL::getEdges() const { return _edges; }
// _____________________________________________________________________________
const POINT& EdgePL::frontHop() const {
if (!_edges.size()) return *_end->pl().getGeom();
return _edges.back()->pl().frontHop();
}
// _____________________________________________________________________________
const POINT& EdgePL::backHop() const {
if (!_edges.size()) return *_start->pl().getGeom();
return _edges.front()->pl().backHop();
}
// _____________________________________________________________________________
const Node* EdgePL::backNode() const { return _end; }
// _____________________________________________________________________________
const Node* EdgePL::frontNode() const { return _start; }
// _____________________________________________________________________________
const LINE* EdgePL::getGeom() const {
if (!_edges.size()) return 0;
if (!_geom.size()) {
const trgraph::Node* l = _start;
for (auto i = _edges.rbegin(); i != _edges.rend(); i++) {
const auto e = *i;
if ((e->getFrom() == l) ^ e->pl().isRev()) {
_geom.insert(_geom.end(), e->pl().getGeom()->begin(),
e->pl().getGeom()->end());
} else {
_geom.insert(_geom.end(), e->pl().getGeom()->rbegin(),
e->pl().getGeom()->rend());
}
l = e->getOtherNd(l);
}
}
return &_geom;
}
// _____________________________________________________________________________
void EdgePL::setStartNode(const trgraph::Node* s) { _start = s; }
// _____________________________________________________________________________
void EdgePL::setEndNode(const trgraph::Node* e) { _end = e; }
// _____________________________________________________________________________
void EdgePL::setStartEdge(const trgraph::Edge* s) { _startE = s; }
// _____________________________________________________________________________
void EdgePL::setEndEdge(const trgraph::Edge* e) { _endE = e; }
// _____________________________________________________________________________
const EdgeCost& EdgePL::getCost() const { return _cost; }
// _____________________________________________________________________________
void EdgePL::setCost(const router::EdgeCost& c) { _cost = c; }
// _____________________________________________________________________________
util::json::Dict EdgePL::getAttrs() const {
util::json::Dict obj;
obj["cost"] = std::to_string(_cost.getValue());
obj["from_edge"] = util::toString(_startE);
obj["to_edge"] = util::toString(_endE);
obj["dummy"] = _edges.size() ? "no" : "yes";
return obj;
}

View file

@ -1,51 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_EDGEPL_H_
#define PFAEDLE_ROUTER_EDGEPL_H_
#include <map>
#include <string>
#include "pfaedle/Def.h"
#include "pfaedle/router/Misc.h"
#include "util/geo/Geo.h"
#include "util/geo/GeoGraph.h"
using util::geograph::GeoEdgePL;
namespace pfaedle {
namespace router {
class EdgePL {
public:
EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {}
const LINE* getGeom() const;
util::json::Dict getAttrs() const;
router::EdgeList* getEdges();
const router::EdgeList& getEdges() const;
void setStartNode(const trgraph::Node* s);
void setEndNode(const trgraph::Node* s);
void setStartEdge(const trgraph::Edge* s);
void setEndEdge(const trgraph::Edge* s);
const router::EdgeCost& getCost() const;
void setCost(const router::EdgeCost& c);
const POINT& frontHop() const;
const POINT& backHop() const;
const trgraph::Node* frontNode() const;
const trgraph::Node* backNode() const;
private:
router::EdgeCost _cost;
// the edges are in this field in REVERSED ORDER!
router::EdgeList _edges;
const trgraph::Node* _start;
const trgraph::Node* _end;
const trgraph::Edge* _startE;
const trgraph::Edge* _endE;
mutable LINE _geom;
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_EDGEPL_H_

View file

@ -1,26 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_GRAPH_H_
#define PFAEDLE_ROUTER_GRAPH_H_
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/router/EdgePL.h"
#include "pfaedle/router/NodePL.h"
#include "util/graph/DirGraph.h"
using util::geo::Point;
using util::geo::Line;
namespace pfaedle {
namespace router {
typedef util::graph::Edge<router::NodePL, router::EdgePL> Edge;
typedef util::graph::Node<router::NodePL, router::EdgePL> Node;
typedef util::graph::DirGraph<router::NodePL, router::EdgePL> Graph;
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_GRAPH_H_

View file

@ -0,0 +1,40 @@
// Copyright 2020, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <utility>
#include <set>
#include "pfaedle/router/HopCache.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/Misc.h"
using pfaedle::router::HopCache;
using pfaedle::trgraph::Edge;
// _____________________________________________________________________________
void HopCache::setMin(const Edge* a, const Edge* b, uint32_t val) {
_cache.set(a, b, val);
}
// _____________________________________________________________________________
void HopCache::setEx(const Edge* a, const Edge* b, uint32_t val) {
int64_t v = val;
_cache.set(a, b, -(v + 1));
}
// _____________________________________________________________________________
void HopCache::setMin(const Edge* a, const std::set<Edge*>& b, uint32_t val) {
for (auto eb : b) _cache.set(a, eb, val);
}
// _____________________________________________________________________________
void HopCache::setMin(const std::set<Edge*>& a, const Edge* b, uint32_t val) {
for (auto ea : a) _cache.set(ea, b, val);
}
// _____________________________________________________________________________
std::pair<uint32_t, bool> HopCache::get(const Edge* a, const Edge* b) const {
int64_t v = _cache.get(a, b);
if (v < 0) return {(-v) - 1, 1};
return {v, 0};
}

View file

@ -0,0 +1,39 @@
// Copyright 2020, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_HOPCACHE_H_
#define PFAEDLE_ROUTER_HOPCACHE_H_
#include <map>
#include <set>
#include <utility>
#include "pfaedle/trgraph/Graph.h"
#include "util/Misc.h"
namespace pfaedle {
namespace router {
class HopCache {
public:
void setMin(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val);
void setMin(const trgraph::Edge* a, const std::set<trgraph::Edge*>& b,
uint32_t val);
void setMin(const std::set<trgraph::Edge*>& a, const trgraph::Edge* b,
uint32_t val);
void setEx(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val);
std::pair<uint32_t, bool> get(const trgraph::Edge* a,
const trgraph::Edge* b) const;
private:
util::SparseMatrix<const trgraph::Edge*, int64_t, 0> _cache;
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_HOPCACHE_H_

View file

@ -9,6 +9,7 @@
#include <string>
#include <unordered_map>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/Route.h"
#include "pfaedle/gtfs/Feed.h"
@ -21,122 +22,79 @@ using ad::cppgtfs::gtfs::Stop;
namespace pfaedle {
namespace router {
struct NodeCand {
trgraph::Node* nd;
double pen;
};
extern double time;
struct EdgeCand {
trgraph::Edge* e;
double pen;
double progr;
POINT point;
int time;
std::vector<size_t> depPrede;
};
struct RoutingOpts {
RoutingOpts()
: fullTurnPunishFac(2000),
: fullTurnPunishFac(1000),
fullTurnAngle(45),
passThruStationsPunish(100),
oneWayPunishFac(1),
oneWayEdgePunish(0),
lineUnmatchedPunishFact(0.5),
lineUnmatchedPunishFact(1),
lineNameFromUnmatchedPunishFact(1),
lineNameToUnmatchedPunishFact(1),
noLinesPunishFact(1),
platformUnmatchedPen(0),
stationDistPenFactor(0),
turnRestrCost(0),
popReachEdge(true),
noSelfHops(true) {}
double fullTurnPunishFac;
uint32_t fullTurnPunishFac;
double fullTurnAngle;
double passThruStationsPunish;
double oneWayPunishFac;
double oneWayEdgePunish;
double lineUnmatchedPunishFact;
double lineNameFromUnmatchedPunishFact;
double lineNameToUnmatchedPunishFact;
double noLinesPunishFact;
double platformUnmatchedPen;
double stationUnmatchedPen;
double stationDistPenFactor;
double nonOsmPen;
double levelPunish[8];
double nonStationPen;
uint32_t turnRestrCost;
bool popReachEdge;
bool noSelfHops;
bool useStations;
double transitionPen;
std::string transPenMethod;
std::string emPenMethod;
std::string statsimiMethod;
};
// _____________________________________________________________________________
inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) {
return fabs(a.fullTurnPunishFac - b.fullTurnPunishFac) < 0.01 &&
return a.fullTurnPunishFac == b.fullTurnPunishFac &&
fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.01 &&
fabs(a.passThruStationsPunish - b.passThruStationsPunish) < 0.01 &&
fabs(a.oneWayPunishFac - b.oneWayPunishFac) < 0.01 &&
fabs(a.oneWayEdgePunish - b.oneWayEdgePunish) < 0.01 &&
fabs(a.lineUnmatchedPunishFact - b.lineUnmatchedPunishFact) < 0.01 &&
fabs(a.lineNameFromUnmatchedPunishFact -
b.lineNameFromUnmatchedPunishFact) < 0.01 &&
fabs(a.lineNameToUnmatchedPunishFact -
b.lineNameToUnmatchedPunishFact) < 0.01 &&
fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.01 &&
fabs(a.platformUnmatchedPen - b.platformUnmatchedPen) < 0.01 &&
fabs(a.stationUnmatchedPen - b.stationUnmatchedPen) < 0.01 &&
fabs(a.stationDistPenFactor - b.stationDistPenFactor) < 0.01 &&
fabs(a.nonOsmPen - b.nonOsmPen) < 0.01 &&
fabs(a.levelPunish[0] - b.levelPunish[0]) < 0.01 &&
fabs(a.levelPunish[1] - b.levelPunish[1]) < 0.01 &&
fabs(a.levelPunish[2] - b.levelPunish[2]) < 0.01 &&
fabs(a.levelPunish[3] - b.levelPunish[3]) < 0.01 &&
fabs(a.levelPunish[4] - b.levelPunish[4]) < 0.01 &&
fabs(a.levelPunish[5] - b.levelPunish[5]) < 0.01 &&
fabs(a.levelPunish[6] - b.levelPunish[6]) < 0.01 &&
fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01 &&
a.popReachEdge == b.popReachEdge && a.noSelfHops == b.noSelfHops;
}
struct EdgeCost {
EdgeCost() : _cost(0) {}
explicit EdgeCost(double cost) : _cost(cost) {}
EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3,
double mDistLvl4, double mDistLvl5, double mDistLvl6,
double mDistLvl7, uint32_t fullTurns, int32_t passThru,
double oneWayMeters, size_t oneWayEdges, double lineUnmatchedMeters,
double reachPen, const RoutingOpts* o) {
if (!o) {
_cost = mDist + reachPen;
} else {
_cost = mDist * o->levelPunish[0] + mDistLvl1 * o->levelPunish[1] +
mDistLvl2 * o->levelPunish[2] + mDistLvl3 * o->levelPunish[3] +
mDistLvl4 * o->levelPunish[4] + mDistLvl5 * o->levelPunish[5] +
mDistLvl6 * o->levelPunish[6] + mDistLvl7 * o->levelPunish[7] +
oneWayMeters * o->oneWayPunishFac +
oneWayEdges * o->oneWayEdgePunish +
lineUnmatchedMeters * o->lineUnmatchedPunishFact +
fullTurns * o->fullTurnPunishFac +
passThru * o->passThruStationsPunish + reachPen;
}
}
float _cost;
double getValue() const { return _cost; }
};
inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) {
return EdgeCost(a.getValue() + b.getValue());
}
inline bool operator<=(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() <= b.getValue();
}
inline bool operator==(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() == b.getValue();
}
inline bool operator>(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() > b.getValue();
}
template <typename F>
inline bool angSmaller(const Point<F>& f, const Point<F>& m, const Point<F>& t,
double ang) {
if (util::geo::innerProd(m, f, t) < ang) return 1;
return 0;
a.turnRestrCost == b.turnRestrCost &&
fabs(a.transitionPen - b.transitionPen) < 0.01 &&
fabs(a.nonStationPen - b.nonStationPen) < 0.01 &&
a.transPenMethod == b.transPenMethod &&
a.emPenMethod == b.emPenMethod &&
a.statsimiMethod == b.statsimiMethod &&
a.useStations == b.useStations && a.popReachEdge == b.popReachEdge &&
a.noSelfHops == b.noSelfHops;
}
typedef std::set<trgraph::Node*> NodeSet;
typedef std::set<trgraph::Edge*> EdgeSet;
typedef std::unordered_map<const Stop*, trgraph::Node*> FeedStops;
typedef std::vector<NodeCand> NodeCandGroup;
typedef std::vector<NodeCandGroup> NodeCandRoute;
typedef std::vector<EdgeCand> EdgeCandGroup;
typedef std::vector<EdgeCandGroup> EdgeCandMap;
typedef std::vector<EdgeCandGroup> EdgeCandRoute;
typedef std::vector<trgraph::Edge*> EdgeList;
@ -144,14 +102,19 @@ typedef std::vector<trgraph::Node*> NodeList;
struct EdgeListHop {
EdgeList edges;
const trgraph::Node* start;
const trgraph::Node* end;
const trgraph::Edge* start;
const trgraph::Edge* end;
double progrStart;
double progrEnd;
POINT pointStart;
POINT pointEnd;
};
typedef std::vector<EdgeListHop> EdgeListHops;
typedef std::set<Route::TYPE> MOTs;
// _____________________________________________________________________________
inline MOTs motISect(const MOTs& a, const MOTs& b) {
MOTs ret;
for (auto mot : a)
@ -159,6 +122,7 @@ inline MOTs motISect(const MOTs& a, const MOTs& b) {
return ret;
}
// _____________________________________________________________________________
inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed,
const MOTs mots,
const std::string& tid) {
@ -183,10 +147,29 @@ inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed,
return ret;
}
// _____________________________________________________________________________
inline std::string getMotStr(const MOTs& mots) {
MOTs tmp = mots;
bool first = false;
std::string motStr;
for (const auto& mot : mots) {
std::string names[11] = {"tram", "subway", "rail", "bus",
"ferry", "cablecar", "gondola", "funicular",
"coach", "trolleybus", "monorail"};
for (const auto& n : names) {
const auto& types = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(n);
const auto& isect = motISect(tmp, types);
if (isect.size() == types.size()) {
if (first) motStr += ", ";
motStr += "{" + n + "}";
first = true;
for (const auto& mot : isect) tmp.erase(mot);
}
}
for (const auto& mot : tmp) {
if (first) motStr += ", ";
motStr += "<" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot) + ">";
first = true;

View file

@ -1,40 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_NODEPL_H_
#define PFAEDLE_ROUTER_NODEPL_H_
#include <map>
#include <string>
#include "pfaedle/trgraph/Graph.h"
#include "util/geo/GeoGraph.h"
#include "util/geo/Geo.h"
#include "pfaedle/Def.h"
using util::geograph::GeoNodePL;
namespace pfaedle {
namespace router {
class NodePL {
public:
NodePL() : _n(0) {}
NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT
const POINT* getGeom() const {
return !_n ? 0 : _n->pl().getGeom();
}
util::json::Dict getAttrs() const {
if (_n) return _n->pl().getAttrs();
return util::json::Dict();
}
private:
const pfaedle::trgraph::Node* _n;
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_NODEPL_H_

View file

@ -1,643 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_thread_num() 0
#define omp_get_num_procs() 1
#endif
#include <algorithm>
#include <fstream>
#include <limits>
#include <map>
#include <set>
#include <unordered_map>
#include <utility>
#include <vector>
#include "pfaedle/router/Comp.h"
#include "pfaedle/router/Router.h"
#include "pfaedle/router/RoutingAttrs.h"
#include "util/geo/output/GeoGraphJsonOutput.h"
#include "util/graph/Dijkstra.h"
#include "util/graph/EDijkstra.h"
#include "util/log/Log.h"
using pfaedle::router::Router;
using pfaedle::router::EdgeCost;
using pfaedle::router::CostFunc;
using pfaedle::router::DistHeur;
using pfaedle::router::NCostFunc;
using pfaedle::router::NDistHeur;
using pfaedle::router::CombCostFunc;
using pfaedle::router::EdgeListHop;
using pfaedle::router::EdgeListHops;
using pfaedle::router::RoutingOpts;
using pfaedle::router::RoutingAttrs;
using pfaedle::router::HopBand;
using pfaedle::router::NodeCandRoute;
using util::graph::EDijkstra;
using util::graph::Dijkstra;
using util::geo::webMercMeterDist;
// _____________________________________________________________________________
EdgeCost NCostFunc::operator()(const trgraph::Node* from,
const trgraph::Edge* e,
const trgraph::Node* to) const {
UNUSED(to);
if (!from) return EdgeCost();
int oneway = e->pl().oneWay() == 2;
int32_t stationSkip = 0;
return EdgeCost(e->pl().lvl() == 0 ? e->pl().getLength() : 0,
e->pl().lvl() == 1 ? e->pl().getLength() : 0,
e->pl().lvl() == 2 ? e->pl().getLength() : 0,
e->pl().lvl() == 3 ? e->pl().getLength() : 0,
e->pl().lvl() == 4 ? e->pl().getLength() : 0,
e->pl().lvl() == 5 ? e->pl().getLength() : 0,
e->pl().lvl() == 6 ? e->pl().getLength() : 0,
e->pl().lvl() == 7 ? e->pl().getLength() : 0, 0, stationSkip,
e->pl().getLength() * oneway, oneway, 0, 0, &_rOpts);
}
// _____________________________________________________________________________
EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n,
const trgraph::Edge* to) const {
if (!from) return EdgeCost();
uint32_t fullTurns = 0;
int oneway = from->pl().oneWay() == 2;
int32_t stationSkip = 0;
if (n) {
if (from->getFrom() == to->getTo() && from->getTo() == to->getFrom()) {
// trivial full turn
fullTurns = 1;
} else if (n->getDeg() > 2) {
// otherwise, only intersection angles will be punished
fullTurns = router::angSmaller(from->pl().backHop(), *n->pl().getGeom(),
to->pl().frontHop(), _rOpts.fullTurnAngle);
}
if (from->pl().isRestricted() && !_res.may(from, to, n)) oneway = 1;
// for debugging
n->pl().setVisited();
if (_tgGrp && n->pl().getSI() && n->pl().getSI()->getGroup() != _tgGrp)
stationSkip = 1;
}
double transitLinePen = transitLineCmp(from->pl());
return EdgeCost(from->pl().lvl() == 0 ? from->pl().getLength() : 0,
from->pl().lvl() == 1 ? from->pl().getLength() : 0,
from->pl().lvl() == 2 ? from->pl().getLength() : 0,
from->pl().lvl() == 3 ? from->pl().getLength() : 0,
from->pl().lvl() == 4 ? from->pl().getLength() : 0,
from->pl().lvl() == 5 ? from->pl().getLength() : 0,
from->pl().lvl() == 6 ? from->pl().getLength() : 0,
from->pl().lvl() == 7 ? from->pl().getLength() : 0, fullTurns,
stationSkip, from->pl().getLength() * oneway, oneway,
from->pl().getLength() * transitLinePen, 0, &_rOpts);
}
// _____________________________________________________________________________
double CostFunc::transitLineCmp(const trgraph::EdgePL& e) const {
if (_rAttrs.shortName.empty() && _rAttrs.toString.empty() &&
_rAttrs.fromString.empty())
return 0;
double best = 1;
for (const auto* l : e.getLines()) {
double cur = _rAttrs.simi(l);
if (cur < 0.0001) return 0;
if (cur < best) best = cur;
}
return best;
}
// _____________________________________________________________________________
NDistHeur::NDistHeur(const RoutingOpts& rOpts,
const std::set<trgraph::Node*>& tos)
: _rOpts(rOpts), _maxCentD(0) {
size_t c = 0;
double x = 0, y = 0;
for (auto to : tos) {
x += to->pl().getGeom()->getX();
y += to->pl().getGeom()->getY();
c++;
}
x /= c;
y /= c;
_center = POINT(x, y);
for (auto to : tos) {
double cur = webMercMeterDist(*to->pl().getGeom(), _center);
if (cur > _maxCentD) _maxCentD = cur;
}
}
// _____________________________________________________________________________
DistHeur::DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos)
: _rOpts(rOpts), _lvl(minLvl), _maxCentD(0) {
size_t c = 0;
double x = 0, y = 0;
for (auto to : tos) {
x += to->getFrom()->pl().getGeom()->getX();
y += to->getFrom()->pl().getGeom()->getY();
c++;
}
x /= c;
y /= c;
_center = POINT(x, y);
for (auto to : tos) {
double cur = webMercMeterDist(*to->getFrom()->pl().getGeom(), _center) *
_rOpts.levelPunish[_lvl];
if (cur > _maxCentD) _maxCentD = cur;
}
}
// _____________________________________________________________________________
EdgeCost DistHeur::operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const {
UNUSED(b);
double cur = webMercMeterDist(*a->getFrom()->pl().getGeom(), _center) *
_rOpts.levelPunish[_lvl];
return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
// _____________________________________________________________________________
EdgeCost NDistHeur::operator()(const trgraph::Node* a,
const std::set<trgraph::Node*>& b) const {
UNUSED(b);
double cur = webMercMeterDist(*a->pl().getGeom(), _center);
return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
// _____________________________________________________________________________
double CombCostFunc::operator()(const router::Edge* from, const router::Node* n,
const router::Edge* to) const {
UNUSED(n);
UNUSED(from);
return to->pl().getCost().getValue();
}
// _____________________________________________________________________________
Router::Router(size_t numThreads, bool caching)
: _cache(numThreads), _caching(caching) {
for (size_t i = 0; i < numThreads; i++) {
_cache[i] = new Cache();
}
}
// _____________________________________________________________________________
Router::~Router() {
for (size_t i = 0; i < _cache.size(); i++) {
delete _cache[i];
}
}
// _____________________________________________________________________________
bool Router::compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const {
for (auto n1 : a) {
for (auto n2 : b) {
if (n1.e->getFrom()->pl().getComp() == n2.e->getFrom()->pl().getComp())
return true;
}
}
return false;
}
// _____________________________________________________________________________
HopBand Router::getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
assert(a.size());
assert(b.size());
double pend = 0;
for (size_t i = 0; i < a.size(); i++) {
for (size_t j = 0; j < b.size(); j++) {
double d = webMercMeterDist(*a[i].e->getFrom()->pl().getGeom(),
*b[j].e->getFrom()->pl().getGeom());
if (d > pend) pend = d;
}
}
LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters";
const trgraph::StatGroup* tgGrpTo = 0;
if (b.begin()->e->getFrom()->pl().getSI())
tgGrpTo = b.begin()->e->getFrom()->pl().getSI()->getGroup();
CostFunc costF(rAttrs, rOpts, rest, tgGrpTo, pend * 50);
std::set<trgraph::Edge *> from, to;
for (auto e : a) from.insert(e.e);
for (auto e : b) to.insert(e.e);
LOG(VDEBUG) << "Doing pilot run between " << from.size() << "->" << to.size()
<< " edge candidates";
EdgeList el;
EdgeCost ret = costF.inf();
DistHeur distH(0, rOpts, to);
if (compConned(a, b))
ret = EDijkstra::shortestPath(from, to, costF, distH, &el);
if (el.size() < 2 && costF.inf() <= ret) {
LOG(VDEBUG) << "Pilot run: no connection between candidate groups,"
<< " setting max distance to 1";
return HopBand{0, 1, 0, 0};
}
// cache the found path, will save a few dijkstra iterations
nestedCache(&el, from, costF, rAttrs);
auto na = el.back()->getFrom();
auto nb = el.front()->getFrom();
double maxStrD = 0;
for (auto e : to) {
double d = webMercMeterDist(*el.front()->getFrom()->pl().getGeom(),
*e->getTo()->pl().getGeom());
if (d > maxStrD) maxStrD = d;
}
// TODO(patrick): derive the punish level here automatically
double maxD = std::max(ret.getValue(), pend * rOpts.levelPunish[2]) * 3 +
rOpts.fullTurnPunishFac + rOpts.platformUnmatchedPen;
double minD = ret.getValue();
LOG(VDEBUG) << "Pilot run: min distance between two groups is "
<< ret.getValue() << " (between nodes " << na << " and " << nb
<< "), using a max routing distance of " << maxD << ". The max"
<< " straight line distance from the pilot target to any other "
"target node was"
<< " " << maxStrD << ".";
return HopBand{minD, maxD, el.front(), maxStrD};
}
// _____________________________________________________________________________
EdgeListHops Router::routeGreedy(const NodeCandRoute& route,
const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
if (route.size() < 2) return EdgeListHops();
EdgeListHops ret(route.size() - 1);
for (size_t i = 0; i < route.size() - 1; i++) {
const trgraph::StatGroup* tgGrp = 0;
std::set<trgraph::Node *> from, to;
for (auto c : route[i]) from.insert(c.nd);
for (auto c : route[i + 1]) to.insert(c.nd);
if (route[i + 1].begin()->nd->pl().getSI())
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
NCostFunc cost(rAttrs, rOpts, rest, tgGrp);
NDistHeur dist(rOpts, to);
NodeList nodesRet;
EdgeListHop hop;
Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet);
if (nodesRet.size() > 1) {
// careful: nodesRet is reversed!
hop.start = nodesRet.back();
hop.end = nodesRet.front();
} else {
// just take the first candidate if no route could be found
hop.start = *from.begin();
hop.end = *to.begin();
}
ret[i] = hop;
}
return ret;
}
// _____________________________________________________________________________
EdgeListHops Router::routeGreedy2(const NodeCandRoute& route,
const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
if (route.size() < 2) return EdgeListHops();
EdgeListHops ret(route.size() - 1);
for (size_t i = 0; i < route.size() - 1; i++) {
const trgraph::StatGroup* tgGrp = 0;
std::set<trgraph::Node *> from, to;
if (i == 0)
for (auto c : route[i]) from.insert(c.nd);
else
from.insert(const_cast<trgraph::Node*>(ret[i - 1].end));
for (auto c : route[i + 1]) to.insert(c.nd);
if (route[i + 1].begin()->nd->pl().getSI())
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
NCostFunc cost(rAttrs, rOpts, rest, tgGrp);
NDistHeur dist(rOpts, to);
NodeList nodesRet;
EdgeListHop hop;
Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet);
if (nodesRet.size() > 1) {
// careful: nodesRet is reversed!
hop.start = nodesRet.back();
hop.end = nodesRet.front();
} else {
// just take the first candidate if no route could be found
hop.start = *from.begin();
hop.end = *to.begin();
}
ret[i] = hop;
}
return ret;
}
// _____________________________________________________________________________
EdgeListHops Router::route(const EdgeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
router::Graph cg;
return Router::route(route, rAttrs, rOpts, rest, &cg);
}
// _____________________________________________________________________________
EdgeListHops Router::route(const EdgeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest,
router::Graph* cgraph) const {
if (route.size() < 2) return EdgeListHops();
EdgeListHops ret(route.size() - 1);
CombCostFunc ccost(rOpts);
router::Node* source = cgraph->addNd();
router::Node* sink = cgraph->addNd();
CombNodeMap nodes;
CombNodeMap nextNodes;
for (size_t i = 0; i < route[0].size(); i++) {
auto e = route[0][i].e;
// we can be sure that each edge is exactly assigned to only one
// node because the transitgraph is directed
nodes[e] = cgraph->addNd(route[0][i].e->getFrom());
cgraph->addEdg(source, nodes[e])
->pl()
.setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
route[0][i].pen, 0));
}
size_t iters = EDijkstra::ITERS;
double itPerSecTot = 0;
size_t n = 0;
for (size_t i = 0; i < route.size() - 1; i++) {
nextNodes.clear();
HopBand hopBand = getHopBand(route[i], route[i + 1], rAttrs, rOpts, rest);
const trgraph::StatGroup* tgGrp = 0;
if (route[i + 1].begin()->e->getFrom()->pl().getSI())
tgGrp = route[i + 1].begin()->e->getFrom()->pl().getSI()->getGroup();
std::set<trgraph::Edge*> froms;
for (const auto& fr : route[i]) froms.insert(fr.e);
for (auto eFr : froms) {
router::Node* cNodeFr = nodes.find(eFr)->second;
EdgeSet tos;
std::map<trgraph::Edge*, router::Edge*> edges;
std::map<trgraph::Edge*, double> pens;
std::unordered_map<trgraph::Edge*, EdgeList*> edgeLists;
std::unordered_map<trgraph::Edge*, EdgeCost> costs;
assert(route[i + 1].size());
for (const auto& to : route[i + 1]) {
auto eTo = to.e;
tos.insert(eTo);
if (!nextNodes.count(eTo))
nextNodes[eTo] = cgraph->addNd(to.e->getFrom());
if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink);
edges[eTo] = cgraph->addEdg(cNodeFr, nextNodes[eTo]);
pens[eTo] = to.pen;
edgeLists[eTo] = edges[eTo]->pl().getEdges();
edges[eTo]->pl().setStartNode(eFr->getFrom());
// for debugging
edges[eTo]->pl().setStartEdge(eFr);
edges[eTo]->pl().setEndNode(to.e->getFrom());
// for debugging
edges[eTo]->pl().setEndEdge(eTo);
}
size_t iters = EDijkstra::ITERS;
auto t1 = TIME();
assert(tos.size());
assert(froms.size());
hops(eFr, froms, tos, tgGrp, edgeLists, &costs, rAttrs, rOpts, rest,
hopBand);
double itPerSec =
(static_cast<double>(EDijkstra::ITERS - iters)) / TOOK(t1, TIME());
n++;
itPerSecTot += itPerSec;
LOG(VDEBUG) << "from " << eFr << ": 1-" << tos.size() << " ("
<< route[i + 1].size() << " nodes) hop took "
<< EDijkstra::ITERS - iters << " iterations, "
<< TOOK(t1, TIME()) << "ms (tput: " << itPerSec << " its/ms)";
for (auto& kv : edges) {
kv.second->pl().setCost(
EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) +
costs[kv.first]);
if (rOpts.popReachEdge && kv.second->pl().getEdges()->size()) {
if (kv.second->pl().getEdges() &&
kv.second->pl().getEdges()->size()) {
// the reach edge is included, but we dont want it in the geometry
kv.second->pl().getEdges()->erase(
kv.second->pl().getEdges()->begin());
}
}
}
}
std::swap(nodes, nextNodes);
}
LOG(VDEBUG) << "Hops took " << EDijkstra::ITERS - iters << " iterations,"
<< " average tput was " << (itPerSecTot / n) << " its/ms";
iters = EDijkstra::ITERS;
std::vector<router::Edge*> res;
EDijkstra::shortestPath(source, sink, ccost, &res);
size_t j = 0;
LOG(VDEBUG) << "Optim graph solve took " << EDijkstra::ITERS - iters
<< " iterations.";
for (auto i = res.rbegin(); i != res.rend(); i++) {
const auto e = *i;
if (e->getFrom() != source && e->getTo() != sink) {
assert(e->pl().frontNode());
assert(e->pl().backNode());
ret[j] = EdgeListHop{std::move(*e->pl().getEdges()), e->pl().frontNode(),
e->pl().backNode()};
j++;
}
}
assert(ret.size() == j);
return ret;
}
// _____________________________________________________________________________
EdgeListHops Router::route(const NodeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const {
router::Graph cg;
return Router::route(route, rAttrs, rOpts, rest, &cg);
}
// _____________________________________________________________________________
EdgeListHops Router::route(const NodeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest,
router::Graph* cgraph) const {
EdgeCandRoute r;
for (auto& nCands : route) {
r.emplace_back();
for (auto n : nCands)
for (auto* e : n.nd->getAdjListOut())
r.back().push_back(EdgeCand{e, n.pen});
}
return Router::route(r, rAttrs, rOpts, rest, cgraph);
}
// _____________________________________________________________________________
void Router::hops(trgraph::Edge* from, const std::set<trgraph::Edge*>& froms,
const std::set<trgraph::Edge*> tos,
const trgraph::StatGroup* tgGrp,
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest, HopBand hopB) const {
std::set<trgraph::Edge*> rem;
CostFunc cost(rAttrs, rOpts, rest, tgGrp, hopB.maxD);
const auto& cached = getCachedHops(from, tos, edgesRet, rCosts, rAttrs);
for (auto e : cached) {
// shortcut: if the nodes lie in two different connected components,
// the distance between them is trivially infinite
if ((rOpts.noSelfHops && (e == from || e->getFrom() == from->getFrom())) ||
from->getFrom()->pl().getComp() != e->getTo()->pl().getComp() ||
e->pl().oneWay() == 2 || from->pl().oneWay() == 2) {
(*rCosts)[e] = cost.inf();
} else {
rem.insert(e);
}
}
LOG(VDEBUG) << "From cache: " << tos.size() - rem.size()
<< ", have to cal: " << rem.size();
if (rem.size()) {
DistHeur dist(from->getFrom()->pl().getComp()->minEdgeLvl, rOpts, rem);
const auto& ret = EDijkstra::shortestPath(from, rem, cost, dist, edgesRet);
for (const auto& kv : ret) {
nestedCache(edgesRet.at(kv.first), froms, cost, rAttrs);
(*rCosts)[kv.first] = kv.second;
}
}
}
// _____________________________________________________________________________
void Router::nestedCache(const EdgeList* el,
const std::set<trgraph::Edge*>& froms,
const CostFunc& cost,
const RoutingAttrs& rAttrs) const {
if (!_caching) return;
if (el->size() == 0) return;
// iterate over result edges backwards
EdgeList curEdges;
EdgeCost curCost;
size_t j = 0;
for (auto i = el->begin(); i < el->end(); i++) {
if (curEdges.size()) {
curCost = curCost + cost(*i, (*i)->getTo(), curEdges.back());
}
curEdges.push_back(*i);
if (froms.count(*i)) {
EdgeCost startC = cost(0, 0, *i) + curCost;
cache(*i, el->front(), startC, &curEdges, rAttrs);
j++;
}
}
}
// _____________________________________________________________________________
std::set<pfaedle::trgraph::Edge*> Router::getCachedHops(
trgraph::Edge* from, const std::set<trgraph::Edge*>& tos,
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
const RoutingAttrs& rAttrs) const {
std::set<trgraph::Edge*> ret;
for (auto to : tos) {
if (_caching && (*_cache[omp_get_thread_num()])[rAttrs][from].count(to)) {
const auto& cv = (*_cache[omp_get_thread_num()])[rAttrs][from][to];
(*rCosts)[to] = cv.first;
*edgesRet.at(to) = cv.second;
} else {
ret.insert(to);
}
}
return ret;
}
// _____________________________________________________________________________
void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
EdgeList* edges, const RoutingAttrs& rAttrs) const {
if (!_caching) return;
if (from == to) return;
(*_cache[omp_get_thread_num()])[rAttrs][from][to] =
std::pair<EdgeCost, EdgeList>(c, *edges);
}
// _____________________________________________________________________________
size_t Router::getCacheNumber() const { return _cache.size(); }

View file

@ -7,197 +7,95 @@
#include <limits>
#include <map>
#include <mutex>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "pfaedle/Def.h"
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Graph.h"
#include "pfaedle/router/HopCache.h"
#include "pfaedle/router/Misc.h"
#include "pfaedle/router/RoutingAttrs.h"
#include "pfaedle/router/TripTrie.h"
#include "pfaedle/router/Weights.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/Misc.h"
#include "util/geo/Geo.h"
#include "util/graph/Dijkstra.h"
#include "util/graph/EDijkstra.h"
using util::graph::EDijkstra;
using util::graph::Dijkstra;
namespace pfaedle {
namespace router {
typedef std::unordered_map<const trgraph::Edge*, router::Node*> CombNodeMap;
constexpr static uint32_t ROUTE_INF = std::numeric_limits<uint32_t>::max();
constexpr static double DBL_INF = std::numeric_limits<double>::infinity();
constexpr static size_t NO_PREDE = std::numeric_limits<size_t>::max();
constexpr static int MAX_ROUTE_COST_DOUBLING_STEPS = 3;
typedef std::pair<size_t, size_t> HId;
typedef std::map<
RoutingAttrs,
std::unordered_map<const trgraph::Edge*,
std::unordered_map<const trgraph::Edge*,
std::pair<EdgeCost, EdgeList> > > >
Cache;
typedef std::vector<double> LayerCostsDAG;
typedef std::vector<LayerCostsDAG> CostsDAG;
typedef std::vector<std::vector<size_t>> PredeDAG;
struct HopBand {
double minD;
double maxD;
const trgraph::Edge* nearest;
double maxInGrpDist;
};
typedef std::unordered_map<const trgraph::Edge*,
std::unordered_map<const trgraph::Edge*, uint32_t>>
EdgeCostMatrix;
typedef std::unordered_map<const trgraph::Edge*,
std::unordered_map<const trgraph::Edge*, double>>
EdgeDistMatrix;
typedef util::graph::EDijkstra::EList<trgraph::NodePL, trgraph::EdgePL> TrEList;
struct CostFunc
: public EDijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp,
double max)
: _rAttrs(rAttrs),
_rOpts(rOpts),
_res(res),
_tgGrp(tgGrp),
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, max, 0) {}
typedef std::vector<std::pair<std::pair<size_t, size_t>, uint32_t>> CostMatrix;
const RoutingAttrs& _rAttrs;
const RoutingOpts& _rOpts;
const osm::Restrictor& _res;
const trgraph::StatGroup* _tgGrp;
EdgeCost _inf;
EdgeCost operator()(const trgraph::Edge* from, const trgraph::Node* n,
const trgraph::Edge* to) const;
EdgeCost inf() const { return _inf; }
double transitLineCmp(const trgraph::EdgePL& e) const;
};
struct NCostFunc
: public Dijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
NCostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp)
: _rAttrs(rAttrs),
_rOpts(rOpts),
_res(res),
_tgGrp(tgGrp),
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
std::numeric_limits<double>::infinity(), 0) {}
const RoutingAttrs& _rAttrs;
const RoutingOpts& _rOpts;
const osm::Restrictor& _res;
const trgraph::StatGroup* _tgGrp;
EdgeCost _inf;
EdgeCost operator()(const trgraph::Node* from, const trgraph::Edge* e,
const trgraph::Node* to) const;
EdgeCost inf() const { return _inf; }
double transitLineCmp(const trgraph::EdgePL& e) const;
};
struct DistHeur
: public EDijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos);
const RoutingOpts& _rOpts;
uint8_t _lvl;
POINT _center;
double _maxCentD;
EdgeCost operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const;
};
struct NDistHeur
: public Dijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
NDistHeur(const RoutingOpts& rOpts, const std::set<trgraph::Node*>& tos);
const RoutingOpts& _rOpts;
POINT _center;
double _maxCentD;
EdgeCost operator()(const trgraph::Node* a,
const std::set<trgraph::Node*>& b) const;
};
struct CombCostFunc
: public EDijkstra::CostFunc<router::NodePL, router::EdgePL, double> {
explicit CombCostFunc(const RoutingOpts& rOpts) : _rOpts(rOpts) {}
const RoutingOpts& _rOpts;
double operator()(const router::Edge* from, const router::Node* n,
const router::Edge* to) const;
double inf() const { return std::numeric_limits<double>::infinity(); }
class Router {
public:
virtual ~Router() = default;
virtual std::map<size_t, EdgeListHops> route(
const TripTrie<pfaedle::gtfs::Trip>* trie, const EdgeCandMap& ecm,
const RoutingOpts& rOpts, const osm::Restrictor& rest, HopCache* hopCache,
bool noFastHops) const = 0;
};
/*
* Finds the most likely route of schedule-based vehicle between stops in a
* physical transportation network
*/
class Router {
template <typename TW>
class RouterImpl : public Router {
public:
// Init this router with caches for numThreads threads
explicit Router(size_t numThreads, bool caching);
~Router();
// Find the most likely path through the graph for a node candidate route.
EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts, const osm::Restrictor& rest,
router::Graph* cgraph) const;
// Find the most likely path through the graph for an edge candidate route.
EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts, const osm::Restrictor& rest,
router::Graph* cgraph) const;
// Find the most likely path through cgraph for a node candidate route, but
// based on a greedy node to node approach
EdgeListHops routeGreedy(const NodeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
// Find the most likely path through cgraph for a node candidate route, but
// based on a greedy node to node set approach
EdgeListHops routeGreedy2(const NodeCandRoute& route,
const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
// Return the number of thread caches this router was initialized with
size_t getCacheNumber() const;
// Find the most likely path through the graph for a trip trie.
virtual std::map<size_t, EdgeListHops> route(
const TripTrie<pfaedle::gtfs::Trip>* trie, const EdgeCandMap& ecm,
const RoutingOpts& rOpts, const osm::Restrictor& rest, HopCache* hopCache,
bool noFastHops) const;
private:
mutable std::vector<Cache*> _cache;
bool _caching;
HopBand getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& b,
void hops(const EdgeCandGroup& from, const EdgeCandGroup& to,
CostMatrix* rCosts, CostMatrix* dists, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts, const osm::Restrictor& rest,
HopCache* hopCache, uint32_t maxCost) const;
void hopsFast(const EdgeCandGroup& from, const EdgeCandGroup& to,
const LayerCostsDAG& initCosts, CostMatrix* rCosts,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
const osm::Restrictor& rest,
void hops(trgraph::Edge* from, const std::set<trgraph::Edge*>& froms,
const std::set<trgraph::Edge*> to, const trgraph::StatGroup* tgGrp,
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest, HopBand hopB) const;
HopCache* hopCache, uint32_t maxCost) const;
std::set<trgraph::Edge*> getCachedHops(
trgraph::Edge* from, const std::set<trgraph::Edge*>& to,
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
const RoutingAttrs& rAttrs) const;
bool connected(const EdgeCand& from, const EdgeCandGroup& tos) const;
bool connected(const EdgeCandGroup& froms, const EdgeCand& to) const;
void cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
EdgeList* edges, const RoutingAttrs& rAttrs) const;
bool cacheDrop(
void nestedCache(const EdgeList* el, const std::set<trgraph::Edge*>& froms,
const CostFunc& cost, const RoutingAttrs& rAttrs) const;
HopCache* hopCache, const std::set<trgraph::Edge*>& froms,
const trgraph::Edge* to, uint32_t maxCost) const;
bool compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const;
uint32_t addNonOverflow(uint32_t a, uint32_t b) const;
};
#include "pfaedle/router/Router.tpp"
} // namespace router
} // namespace pfaedle

View file

@ -0,0 +1,629 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifdef _OPENMP
#include <omp.h>
#else
#define omp_get_thread_num() 0
#define omp_get_num_procs() 1
#endif
#include <limits>
#include <map>
#include <set>
#include <stack>
#include <unordered_map>
#include <utility>
#include <vector>
using util::graph::EDijkstra;
// _____________________________________________________________________________
template <typename TW>
std::map<size_t, EdgeListHops> RouterImpl<TW>::route(
const TripTrie<pfaedle::gtfs::Trip>* trie, const EdgeCandMap& ecm,
const RoutingOpts& rOpts, const osm::Restrictor& rest, HopCache* hopCache,
bool noFastHops) const {
std::map<size_t, EdgeListHops> ret;
// the current node costs in our DAG
CostsDAG costsDAG(trie->getNds().size());
PredeDAG predeDAG(trie->getNds().size());
std::vector<double> maxCosts(trie->getNds().size());
// skip the root node, init all to inf
for (size_t nid = 1; nid < trie->getNds().size(); nid++) {
costsDAG[nid].resize(ecm.at(nid).size(), DBL_INF);
predeDAG[nid].resize(ecm.at(nid).size(), NO_PREDE);
}
std::stack<size_t> st;
// init cost of all first childs
for (size_t cnid : trie->getNd(0).childs) {
st.push(cnid);
for (size_t frId = 0; frId < ecm.at(cnid).size(); frId++) {
costsDAG[cnid][frId] = ecm.at(cnid)[frId].pen;
}
}
while (!st.empty()) {
size_t frTrNid = st.top();
st.pop();
const auto& frTrNd = trie->getNd(frTrNid);
// determine the max speed for this hop
double maxSpeed = 0;
for (size_t nid = 0; nid < ecm.at(frTrNid).size(); nid++) {
if (!ecm.at(frTrNid)[nid].e) continue;
if (ecm.at(frTrNid)[nid].e->getFrom()->pl().getComp().maxSpeed > maxSpeed)
maxSpeed = ecm.at(frTrNid)[nid].e->getFrom()->pl().getComp().maxSpeed;
}
for (size_t toTrNid : trie->getNd(frTrNid).childs) {
CostMatrix costM, dists;
const auto& toTrNd = trie->getNd(toTrNid);
if (frTrNd.arr && !toTrNd.arr) {
for (size_t toId = 0; toId < costsDAG[toTrNid].size(); toId++) {
auto toCand = ecm.at(toTrNid)[toId];
for (size_t frId : toCand.depPrede) {
double newC = costsDAG[frTrNid][frId] + ecm.at(toTrNid)[toId].pen;
if (newC < costsDAG[toTrNid][toId]) {
costsDAG[toTrNid][toId] = newC;
predeDAG[toTrNid][toId] = frId;
}
}
}
st.push(toTrNid);
continue;
}
const double avgDepT = frTrNd.accTime / frTrNd.trips;
const double avgArrT = toTrNd.accTime / toTrNd.trips;
double hopDist = 0;
hopDist = util::geo::haversine(frTrNd.lat, frTrNd.lng, toTrNd.lat,
toTrNd.lng);
double minTime = hopDist / maxSpeed;
double hopTime = avgArrT - avgDepT;
if (hopTime < minTime) hopTime = minTime;
uint32_t newMaxCost = TW::maxCost(hopTime, rOpts);
uint32_t maxCost = newMaxCost;
bool found = false;
int step = 0;
while (!found && step <= MAX_ROUTE_COST_DOUBLING_STEPS) {
maxCosts[toTrNid] = newMaxCost;
maxCost = newMaxCost;
// calculate n x n hops between layers
if (noFastHops || !TW::ALLOWS_FAST_ROUTE) {
hops(ecm.at(frTrNid), ecm.at(toTrNid), &costM, &dists, toTrNd.rAttrs,
rOpts, rest, hopCache, maxCost);
} else {
hopsFast(ecm.at(frTrNid), ecm.at(toTrNid), costsDAG[frTrNid], &costM,
toTrNd.rAttrs, rOpts, rest, hopCache, maxCost);
}
for (size_t matrixI = 0; matrixI < costM.size(); matrixI++) {
const auto& mVal = costM[matrixI];
const size_t frId = mVal.first.first;
const size_t toId = mVal.first.second;
const uint32_t c = mVal.second;
double mDist = 0;
// the dists and the costM matrices have entries at exactly the same
// loc
if (TW::NEED_DIST) mDist = dists[matrixI].second;
// calculate the transition weights
const double depT = ecm.at(frTrNid)[frId].time;
const double arrT = ecm.at(toTrNid)[toId].time;
const double w = TW::weight(c, mDist, arrT - depT, hopDist, rOpts);
// update costs to successors in next layer
double newC = costsDAG[frTrNid][frId] + ecm.at(toTrNid)[toId].pen + w;
if (newC < costsDAG[toTrNid][toId]) {
costsDAG[toTrNid][toId] = newC;
predeDAG[toTrNid][toId] = frId;
found = true;
}
}
if (newMaxCost <= std::numeric_limits<uint32_t>::max() / 2)
newMaxCost *= 2;
else
newMaxCost = std::numeric_limits<uint32_t>::max();
if (newMaxCost == maxCost) break;
step++;
}
if (!found) {
// write the cost for the NULL candidates as a fallback
for (size_t frNid = 0; frNid < ecm.at(frTrNid).size(); frNid++) {
double newC = costsDAG[frTrNid][frNid] + maxCost * 100;
// in the time expanded case, there might be multiple null cands
size_t nullCId = 0;
while (nullCId < ecm.at(toTrNid).size() &&
!ecm.at(toTrNid)[nullCId].e) {
if (newC < costsDAG[toTrNid][nullCId]) {
predeDAG[toTrNid][nullCId] = frNid;
costsDAG[toTrNid][nullCId] = newC;
}
nullCId++;
}
}
// for the remaining, write dummy edges
for (size_t frNid = 0; frNid < ecm.at(frTrNid).size(); frNid++) {
// skip NULL candidates
size_t toNid = 1;
while (toNid < ecm.at(toTrNid).size() && !ecm.at(toTrNid)[toNid].e)
toNid++;
for (; toNid < ecm.at(toTrNid).size(); toNid++) {
double newC = costsDAG[frTrNid][frNid] + ecm.at(toTrNid)[toNid].pen;
if (newC < costsDAG[toTrNid][toNid]) {
predeDAG[toTrNid][toNid] = frNid;
costsDAG[toTrNid][toNid] = newC;
}
}
}
}
st.push(toTrNid);
}
}
// update sink costs
std::unordered_map<size_t, double> sinkCosts;
std::unordered_map<size_t, size_t> frontIds;
for (auto leaf : trie->getNdTrips()) {
sinkCosts[leaf.first] = DBL_INF;
frontIds[leaf.first] = 0;
for (size_t lastId = 0; lastId < ecm.at(leaf.first).size(); lastId++) {
double nCost = costsDAG[leaf.first][lastId];
if (nCost < sinkCosts[leaf.first]) {
frontIds[leaf.first] = lastId;
sinkCosts[leaf.first] = nCost;
}
}
}
// retrieve edges
for (auto leaf : trie->getNdTrips()) {
const auto leafNid = leaf.first;
auto curTrieNid = leafNid;
while (predeDAG[curTrieNid][frontIds[leafNid]] != NO_PREDE) {
const auto curTrieParNid = trie->getNd(curTrieNid).parent;
const auto frId = predeDAG[curTrieNid][frontIds[leafNid]];
const auto toId = frontIds[leafNid];
const auto frTrNd = trie->getNd(curTrieParNid);
const auto toTrNd = trie->getNd(curTrieNid);
// skip in-node hops
if (frTrNd.arr && !toTrNd.arr) {
frontIds[leafNid] = frId;
curTrieNid = curTrieParNid;
continue;
}
std::vector<trgraph::Edge*> edgs;
const auto& fr = ecm.at(curTrieParNid)[frId];
const auto& to = ecm.at(curTrieNid)[toId];
// for subtracting and adding progression costs
typename TW::CostFunc costPr(toTrNd.rAttrs, rOpts, rest, ROUTE_INF);
if (fr.e && to.e) {
// account for max progression start offset, do this exactly like
// in the hops calculation to ensure that we can find the path again
double maxProgrStart = 0;
for (const auto& fr : ecm.at(curTrieParNid)) {
if (!fr.e) continue;
double progrStart = 0;
if (fr.progr > 0) progrStart = costPr(fr.e, 0, 0) * fr.progr;
if (progrStart > maxProgrStart) maxProgrStart = progrStart;
}
const double maxCostRt = maxCosts[curTrieNid] + maxProgrStart;
uint32_t maxCostRtInt = maxCostRt;
// avoid overflow
if (maxCostRt >= std::numeric_limits<uint32_t>::max()) {
maxCostRtInt = std::numeric_limits<uint32_t>::max();
}
typename TW::CostFunc cost(toTrNd.rAttrs, rOpts, rest, maxCostRtInt);
typename TW::DistHeur distH(fr.e->getFrom()->pl().getComp().maxSpeed,
rOpts, {to.e});
const double c =
EDijkstra::shortestPath(fr.e, to.e, cost, distH, &edgs);
if (c < maxCostRtInt) {
// a path was found, use it
ret[leafNid].push_back(
{edgs, fr.e, to.e, fr.progr, to.progr, {}, {}});
} else {
// no path was found, which is marked by an empty edge list
ret[leafNid].push_back({{}, fr.e, to.e, fr.progr, to.progr, {}, {}});
}
} else {
// fallback to the position given in candidate
if (fr.e) {
ret[leafNid].push_back({edgs, fr.e, 0, fr.progr, 0, {}, to.point});
} else if (to.e) {
ret[leafNid].push_back({edgs, 0, to.e, 0, to.progr, fr.point, {}});
} else {
ret[leafNid].push_back({edgs, 0, 0, 0, 0, fr.point, to.point});
}
}
frontIds[leafNid] = frId;
curTrieNid = curTrieParNid;
}
}
return ret;
}
// _____________________________________________________________________________
template <typename TW>
void RouterImpl<TW>::hops(const EdgeCandGroup& froms, const EdgeCandGroup& tos,
CostMatrix* rCosts, CostMatrix* dists,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest, HopCache* hopCache,
uint32_t maxCost) const {
// standard 1 -> n approach
std::set<trgraph::Edge*> eFrs;
for (const auto& from : froms) {
if (!from.e) continue;
eFrs.insert(from.e);
}
std::set<trgraph::Edge*> eTos;
for (const auto& to : tos) {
if (!to.e) continue;
eTos.insert(to.e);
}
EdgeCostMatrix ecm;
EdgeDistMatrix ecmDist;
// account for max progression start offset
double maxProgrStart = 0;
typename TW::CostFunc cost(rAttrs, rOpts, rest, ROUTE_INF);
for (const auto& fr : froms) {
if (!fr.e) continue;
double progrStart = 0;
if (fr.progr > 0) progrStart = cost(fr.e, 0, 0) * fr.progr;
if (progrStart > maxProgrStart) maxProgrStart = progrStart;
}
maxCost = addNonOverflow(maxCost, maxProgrStart);
typename TW::CostFunc costF(rAttrs, rOpts, rest, maxCost);
for (trgraph::Edge* eFrom : eFrs) {
std::set<trgraph::Edge*> remTos;
for (trgraph::Edge* eTo : eTos) {
// init ecmDist
ecmDist[eFrom][eTo] = ROUTE_INF;
std::pair<uint32_t, bool> cached = {0, 0};
if (hopCache) cached = hopCache->get(eFrom, eTo);
// shortcut: if the nodes lie in two different connected components,
// the distance between them is trivially infinite
if (eFrom->getFrom()->pl().getCompId() !=
eTo->getTo()->pl().getCompId()) {
ecm[eFrom][eTo] = costF.inf();
} else if (cached.second >= costF.inf()) {
ecm[eFrom][eTo] = costF.inf();
} else if (!TW::NEED_DIST && cached.second) {
ecm[eFrom][eTo] = cached.first;
} else {
remTos.insert(eTo);
}
}
if (remTos.size()) {
typename TW::DistHeur distH(eFrom->getFrom()->pl().getComp().maxSpeed,
rOpts, remTos);
std::unordered_map<trgraph::Edge*, TrEList> paths;
std::unordered_map<trgraph::Edge*, TrEList*> pathPtrs;
for (auto to : tos) pathPtrs[to.e] = &paths[to.e];
const auto& costs =
EDijkstra::shortestPath(eFrom, remTos, costF, distH, pathPtrs);
for (const auto& c : costs) {
ecm[eFrom][c.first] = c.second;
if (paths[c.first].size() == 0) {
if (hopCache) hopCache->setMin(eFrom, c.first, maxCost);
continue; // no path found
}
if (hopCache) hopCache->setEx(eFrom, c.first, c.second);
}
if (TW::NEED_DIST) {
for (const auto& c : costs) {
if (!paths[c.first].size()) continue;
double d = 0;
// don't count last edge
for (size_t i = paths[c.first].size() - 1; i > 0; i--) {
d += paths[c.first][i]->pl().getLength();
}
ecmDist[eFrom][c.first] = d;
}
}
}
}
// build return costs
for (size_t frId = 0; frId < froms.size(); frId++) {
auto fr = froms[frId];
if (!fr.e) continue;
auto costFr = costF(fr.e, 0, 0);
for (size_t toId = 0; toId < tos.size(); toId++) {
auto to = tos[toId];
if (!to.e) continue;
uint32_t c = ecm[fr.e][to.e];
if (c >= maxCost) continue;
double dist = 0;
if (TW::NEED_DIST) dist = ecmDist[fr.e][to.e];
if (fr.e == to.e) {
if (fr.progr <= to.progr) {
auto costTo = costF(to.e, 0, 0);
const uint32_t progrCFr = costFr * fr.progr;
const uint32_t progrCTo = costTo * to.progr;
// calculate this in one step to avoid uint32_t underflow below
c += progrCTo - progrCFr;
} else {
// trivial case we can ignore
continue;
}
} else {
// subtract progression cost on first edge
if (fr.progr > 0) {
const uint32_t progrCFr = costFr * fr.progr;
c -= progrCFr;
if (TW::NEED_DIST) dist -= fr.e->pl().getLength() * fr.progr;
}
// add progression cost on last edge
if (to.progr > 0) {
const auto costTo = costF(to.e, 0, 0);
const uint32_t progrCTo = costTo * to.progr;
c += progrCTo;
if (TW::NEED_DIST) dist += to.e->pl().getLength() * to.progr;
}
}
if (c < maxCost - maxProgrStart) {
rCosts->push_back({{frId, toId}, c});
if (TW::NEED_DIST)
dists->push_back({{frId, toId}, static_cast<uint32_t>(dist)});
}
}
}
}
// _____________________________________________________________________________
template <typename TW>
void RouterImpl<TW>::hopsFast(const EdgeCandGroup& froms,
const EdgeCandGroup& tos,
const LayerCostsDAG& rawInitCosts,
CostMatrix* rCosts, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& restr, HopCache* hopCache,
uint32_t maxCost) const {
std::unordered_map<trgraph::Edge*, uint32_t> initCosts;
std::set<trgraph::Edge*> eFrs, eTos;
std::map<trgraph::Edge*, std::vector<size_t>> eFrCands, eToCands;
double maxSpeed = 0;
for (size_t frId = 0; frId < froms.size(); frId++) {
if (rawInitCosts[frId] >= DBL_INF || !connected(froms[frId], tos)) continue;
eFrs.insert(froms[frId].e);
eFrCands[froms[frId].e].push_back(frId);
if (froms[frId].e->getFrom()->pl().getComp().maxSpeed > maxSpeed)
maxSpeed = froms[frId].e->getFrom()->pl().getComp().maxSpeed;
}
for (size_t toId = 0; toId < tos.size(); toId++) {
if (!connected(froms, tos[toId]))
continue; // skip nodes not conn'ed to any <fr>
if (hopCache && cacheDrop(hopCache, eFrs, tos[toId].e, maxCost))
continue; // skip nodes we have already encountered at higher cost
eTos.insert(tos[toId].e);
eToCands[tos[toId].e].push_back(toId);
}
if (eFrs.size() == 0 || eTos.size() == 0) return;
// account for max progression start offset
double maxProgrStart = 0;
typename TW::CostFunc progrCostF(rAttrs, rOpts, restr, ROUTE_INF);
for (const auto& fr : froms) {
if (!fr.e) continue;
double progrStart = 0;
if (fr.progr > 0) progrStart = progrCostF(fr.e, 0, 0) * fr.progr;
if (progrStart > maxProgrStart) maxProgrStart = progrStart;
}
// initialize init doubles
LayerCostsDAG prepInitCosts(froms.size());
for (size_t frId = 0; frId < froms.size(); frId++) {
if (!froms[frId].e || rawInitCosts[frId] >= DBL_INF) continue;
const auto& fr = froms[frId];
// offset by progr start
double progrStart = progrCostF(fr.e, 0, 0) * fr.progr;
prepInitCosts[frId] =
TW::invWeight(rawInitCosts[frId], rOpts) + maxProgrStart - progrStart;
}
// all init costs are inf
for (const auto& fr : froms) initCosts[fr.e] = ROUTE_INF;
// now chose the best offset cost
for (size_t frId = 0; frId < froms.size(); frId++) {
if (!froms[frId].e || rawInitCosts[frId] >= DBL_INF) continue;
const auto& fr = froms[frId];
if (prepInitCosts[frId] < initCosts[fr.e])
initCosts[fr.e] = prepInitCosts[frId];
}
// get max init costs
uint32_t maxInit = 0;
uint32_t minInit = ROUTE_INF;
for (const auto& c : initCosts) {
if (!eFrs.count(c.first)) continue;
if (c.second != ROUTE_INF && c.second > maxInit) maxInit = c.second;
if (c.second < minInit) minInit = c.second;
}
for (auto& c : initCosts) c.second = c.second - minInit;
// account for start offsets
maxCost = addNonOverflow(maxCost, maxProgrStart);
typename TW::CostFunc costF(rAttrs, rOpts, restr,
maxCost + (maxInit - minInit));
std::unordered_map<trgraph::Edge*, TrEList> paths;
std::unordered_map<trgraph::Edge*, TrEList*> pathPtrs;
for (const auto& to : tos) pathPtrs[to.e] = &paths[to.e];
typename TW::DistHeur distH(maxSpeed, rOpts, eTos);
const auto& costs =
EDijkstra::shortestPath(eFrs, eTos, initCosts, maxCost, costF, distH);
for (const auto& c : costs) {
auto toEdg = c.first;
if (c.second.second >= costF.inf()) {
if (hopCache) hopCache->setMin(eFrs, toEdg, maxCost);
continue; // no path found
}
auto fromEdg = c.second.first;
uint32_t cost = c.second.second - initCosts[fromEdg];
if (cost >= maxCost) continue;
for (size_t frId : eFrCands.find(fromEdg)->second) {
const auto& fr = froms[frId];
auto costFr = costF(fr.e, 0, 0);
for (size_t toId : eToCands.find(toEdg)->second) {
const auto& to = tos[toId];
uint32_t wrCost = cost;
if (fr.e == to.e) {
if (fr.progr <= to.progr) {
const auto costTo = costF(to.e, 0, 0);
const uint32_t progrCFr = costFr * fr.progr;
const uint32_t progrCTo = costTo * to.progr;
// calculate this in one step to avoid uint32_t underflow below
wrCost += progrCTo - progrCFr;
} else {
// trivial case we can ignore
continue;
}
} else {
// subtract progression cost on first edge
if (fr.progr > 0) {
const uint32_t progrCFr = costFr * fr.progr;
wrCost -= progrCFr;
}
// add progression cost on last edge
if (to.progr > 0) {
const auto costTo = costF(to.e, 0, 0);
const uint32_t progrCTo = costTo * to.progr;
wrCost += progrCTo;
}
}
if (wrCost < maxCost - maxProgrStart) {
rCosts->push_back({{frId, toId}, wrCost});
}
}
}
}
}
// _____________________________________________________________________________
template <typename TW>
bool RouterImpl<TW>::connected(const EdgeCand& fr,
const EdgeCandGroup& tos) const {
if (!fr.e) return false;
for (const auto& to : tos) {
if (!to.e) continue;
if (fr.e->getFrom()->pl().getCompId() == to.e->getFrom()->pl().getCompId())
return true;
}
return false;
}
// _____________________________________________________________________________
template <typename TW>
bool RouterImpl<TW>::connected(const EdgeCandGroup& froms,
const EdgeCand& to) const {
if (!to.e) return false;
for (const auto& fr : froms) {
if (!fr.e) continue;
if (fr.e->getFrom()->pl().getCompId() == to.e->getFrom()->pl().getCompId())
return true;
}
return false;
}
// _____________________________________________________________________________
template <typename TW>
bool RouterImpl<TW>::cacheDrop(HopCache* hopCache,
const std::set<trgraph::Edge*>& froms,
const trgraph::Edge* to,
uint32_t maxCost) const {
for (auto fr : froms)
if (hopCache->get(fr, to).first <= maxCost) return false;
return true;
}
// _____________________________________________________________________________
template <typename TW>
uint32_t RouterImpl<TW>::addNonOverflow(uint32_t a, uint32_t b) const {
if (a == std::numeric_limits<uint32_t>::max() ||
b == std::numeric_limits<uint32_t>::max())
return std::numeric_limits<uint32_t>::max();
uint32_t res = a + b;
if (res >= a && res >= b) return res;
return std::numeric_limits<uint32_t>::max();
}

View file

@ -5,8 +5,10 @@
#ifndef PFAEDLE_ROUTER_ROUTINGATTRS_H_
#define PFAEDLE_ROUTER_ROUTINGATTRS_H_
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
#include "pfaedle/statsimi-classifier/StatsimiClassifier.h"
#include "pfaedle/trgraph/EdgePL.h"
using pfaedle::trgraph::TransitEdgeLine;
@ -14,40 +16,81 @@ using pfaedle::trgraph::TransitEdgeLine;
namespace pfaedle {
namespace router {
struct LineSimilarity {
bool nameSimilar : 1;
bool fromSimilar : 1;
bool toSimilar : 1;
};
inline bool operator<(const LineSimilarity& a, const LineSimilarity& b) {
return (a.nameSimilar + a.fromSimilar + a.toSimilar) <
(b.nameSimilar + b.fromSimilar + b.toSimilar);
}
struct RoutingAttrs {
RoutingAttrs() : fromString(""), toString(""), shortName(""), _simiCache() {}
std::string fromString;
std::string toString;
RoutingAttrs()
: lineFrom(""), lineTo(), shortName(""), classifier(0), _simiCache() {}
RoutingAttrs(const std::string& shortName, const std::string& lineFrom,
const std::string& lineTo)
: lineFrom(lineFrom),
lineTo({lineTo}),
shortName(shortName),
classifier(0),
_simiCache() {}
std::string lineFrom;
std::vector<std::string> lineTo;
std::string shortName;
mutable std::map<const TransitEdgeLine*, double> _simiCache;
const pfaedle::statsimiclassifier::StatsimiClassifier* classifier;
mutable std::unordered_map<const TransitEdgeLine*, LineSimilarity> _simiCache;
LineSimilarity simi(const TransitEdgeLine* line) const {
// shortcut, if we don't have a line information, classify as similar
if (line->shortName.empty() && line->toStr.empty() && line->fromStr.empty())
return {true, true, true};
// carfull: lower return value = higher similarity
double simi(const TransitEdgeLine* line) const {
auto i = _simiCache.find(line);
if (i != _simiCache.end()) return i->second;
double cur = 1;
LineSimilarity ret{false, false, false};
if (shortName.empty() || router::lineSimi(line->shortName, shortName) > 0.5)
cur -= 0.333333333;
ret.nameSimilar = true;
if (toString.empty() || line->toStr.empty() ||
router::statSimi(line->toStr, toString) > 0.5)
cur -= 0.333333333;
if (lineTo.size() == 0) {
ret.toSimilar = true;
} else {
for (const auto& lTo : lineTo) {
if (lTo.empty() || classifier->similar(line->toStr, lTo)) {
ret.toSimilar = true;
break;
}
}
}
if (fromString.empty() || line->fromStr.empty() ||
router::statSimi(line->fromStr, fromString) > 0.5)
cur -= 0.333333333;
if (lineFrom.empty() || classifier->similar(line->fromStr, lineFrom))
ret.fromSimilar = true;
_simiCache[line] = cur;
_simiCache[line] = ret;
return cur;
return ret;
}
void merge(const RoutingAttrs& other) {
assert(other.lineFrom == lineFrom);
assert(other.shortName == shortName);
for (const auto& l : other.lineTo) {
auto i = std::lower_bound(lineTo.begin(), lineTo.end(), l);
if (i != lineTo.end() && (*i) == l) continue; // already present
lineTo.insert(i, l);
}
}
};
inline bool operator==(const RoutingAttrs& a, const RoutingAttrs& b) {
return a.shortName == b.shortName && a.toString == b.toString &&
a.fromString == b.fromString;
return a.shortName == b.shortName && a.lineFrom == b.lineFrom;
}
inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) {
@ -55,10 +98,8 @@ inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) {
}
inline bool operator<(const RoutingAttrs& a, const RoutingAttrs& b) {
return a.fromString < b.fromString ||
(a.fromString == b.fromString && a.toString < b.toString) ||
(a.fromString == b.fromString && a.toString == b.toString &&
a.shortName < b.shortName);
return a.lineFrom < b.lineFrom ||
(a.lineFrom == b.lineFrom && a.shortName < b.shortName);
}
} // namespace router

File diff suppressed because it is too large Load diff

View file

@ -5,43 +5,46 @@
#ifndef PFAEDLE_ROUTER_SHAPEBUILDER_H_
#define PFAEDLE_ROUTER_SHAPEBUILDER_H_
#include <map>
#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/PfaedleConfig.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Misc.h"
#include "pfaedle/router/Router.h"
#include "pfaedle/router/Stats.h"
#include "pfaedle/router/TripTrie.h"
#include "pfaedle/statsimi-classifier/StatsimiClassifier.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/geo/Geo.h"
namespace pfaedle {
namespace router {
using ad::cppgtfs::gtfs::Stop;
using pfaedle::gtfs::Trip;
using pfaedle::gtfs::Feed;
struct Shape {
router::EdgeListHops hops;
double avgHopDist;
};
typedef std::vector<Trip*> Cluster;
typedef std::vector<Cluster> Clusters;
typedef std::pair<const Stop*, const Stop*> StopPair;
typedef std::unordered_map<const Trip*, router::RoutingAttrs> TripRAttrs;
typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
typedef std::vector<TripTrie<pfaedle::gtfs::Trip>> TripForest;
typedef std::map<router::RoutingAttrs, TripForest> TripForests;
typedef std::pair<const ad::cppgtfs::gtfs::Stop*,
const ad::cppgtfs::gtfs::Stop*>
StopPair;
typedef std::unordered_map<const pfaedle::gtfs::Trip*, router::RoutingAttrs>
TripRAttrs;
typedef std::unordered_map<const trgraph::Edge*,
std::vector<const pfaedle::gtfs::Trip*>>
TrGraphEdgs;
typedef std::map<Route*, std::map<uint32_t, std::vector<gtfs::Trip*>>>
RouteRefColors;
typedef std::unordered_map<const ad::cppgtfs::gtfs::Stop*, EdgeCandGroup>
GrpCache;
/*
* Layer class for the router. Provides an interface for direct usage with
@ -49,76 +52,121 @@ typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
*/
class ShapeBuilder {
public:
ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots,
const config::MotConfig& motCfg, eval::Collector* ecoll,
trgraph::Graph* g, router::FeedStops* stops,
osm::Restrictor* restr, const config::Config& cfg);
ShapeBuilder(
pfaedle::gtfs::Feed* feed, MOTs mots, const config::MotConfig& motCfg,
trgraph::Graph* g, router::FeedStops* stops, osm::Restrictor* restr,
const pfaedle::statsimiclassifier::StatsimiClassifier* classifier,
router::Router* router, const config::Config& cfg);
void shape(pfaedle::netgraph::Graph* ng);
Stats shapeify(pfaedle::netgraph::Graph* outNg);
router::FeedStops* getFeedStops();
const NodeCandGroup& getNodeCands(const Stop* s) const;
// shape single trip
std::pair<std::vector<LINE>, Stats> shapeL(pfaedle::gtfs::Trip* trip);
LINE shapeL(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs);
LINE shapeL(Trip* trip);
pfaedle::router::Shape shape(Trip* trip) const;
pfaedle::router::Shape shape(Trip* trip);
std::map<size_t, EdgeListHops> shapeify(
const TripTrie<pfaedle::gtfs::Trip>* trie, HopCache* hopCache) const;
EdgeListHops shapeify(pfaedle::gtfs::Trip* trip);
const trgraph::Graph* getGraph() const;
static void getGtfsBox(const Feed* feed, const MOTs& mots,
static void getGtfsBox(const pfaedle::gtfs::Feed* feed, const MOTs& mots,
const std::string& tid, bool dropShapes,
osm::BBoxIdx* box);
osm::BBoxIdx* box, double maxSpeed,
std::vector<double>* hopDists, uint8_t verbosity);
private:
Feed* _feed;
ad::cppgtfs::gtfs::Feed* _evalFeed;
pfaedle::gtfs::Feed* _feed;
MOTs _mots;
config::MotConfig _motCfg;
eval::Collector* _ecoll;
config::Config _cfg;
trgraph::Graph* _g;
router::Router _crouter;
router::FeedStops* _stops;
NodeCandGroup _emptyNCG;
EdgeCandGroup _emptyNCG;
size_t _curShpCnt, _numThreads;
size_t _curShpCnt;
std::mutex _shpMutex;
TripRAttrs _rAttrs;
osm::Restrictor* _restr;
const pfaedle::statsimiclassifier::StatsimiClassifier* _classifier;
GrpCache _grpCache;
void buildGraph(router::FeedStops* fStops);
router::Router* _router;
Clusters clusterTrips(Feed* f, MOTs mots);
void writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs,
const Cluster& cluster) const;
void buildTrGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const;
TripForests clusterTrips(pfaedle::gtfs::Feed* f, MOTs mots);
void buildNetGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const;
std::string getFreeShapeId(Trip* t);
std::string getFreeShapeId(pfaedle::gtfs::Trip* t);
ad::cppgtfs::gtfs::Shape getGtfsShape(const EdgeListHops& shp,
pfaedle::gtfs::Trip* t,
size_t numOthers,
const RoutingAttrs& rAttrs,
std::vector<float>* hopDists,
uint32_t* bestColor);
ad::cppgtfs::gtfs::Shape getGtfsShape(const Shape& shp, Trip* t,
std::vector<double>* hopDists);
void setShape(pfaedle::gtfs::Trip* t, const ad::cppgtfs::gtfs::Shape& s,
const std::vector<float>& dists);
void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s,
const std::vector<double>& dists);
EdgeCandGroup getEdgCands(const ad::cppgtfs::gtfs::Stop* s) const;
router::NodeCandRoute getNCR(Trip* trip) const;
double avgHopDist(Trip* trip) const;
const router::RoutingAttrs& getRAttrs(const Trip* trip) const;
const router::RoutingAttrs& getRAttrs(const Trip* trip);
bool routingEqual(Trip* a, Trip* b);
bool routingEqual(const Stop* a, const Stop* b);
router::EdgeListHops route(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs) const;
router::EdgeCandMap getECM(const TripTrie<pfaedle::gtfs::Trip>* trie) const;
std::vector<double> getTransTimes(pfaedle::gtfs::Trip* trip) const;
std::vector<double> getTransDists(pfaedle::gtfs::Trip* trip) const;
const router::RoutingAttrs& getRAttrs(const pfaedle::gtfs::Trip* trip) const;
const router::RoutingAttrs& getRAttrs(const pfaedle::gtfs::Trip* trip);
std::map<size_t, router::EdgeListHops> route(
const TripTrie<pfaedle::gtfs::Trip>* trie, const EdgeCandMap& ecm,
HopCache* hopCache) const;
double emWeight(double mDist) const;
void buildCandCache(const TripForests& clusters);
void buildIndex();
std::vector<LINE> getGeom(const EdgeListHops& shp, const RoutingAttrs& rAttrs,
std::map<uint32_t, double>* colors, Trip* t,
size_t numOthers) const;
double timePen(int candTime, int schedTime) const;
LINE getLine(const EdgeListHop& hop, const RoutingAttrs&,
std::map<uint32_t, double>* colMap) const;
LINE getLine(const trgraph::Edge* edg) const;
std::vector<float> getMeasure(const std::vector<LINE>& lines) const;
trgraph::Edge* deg2reachable(trgraph::Edge* e,
std::set<trgraph::Edge*> edgs) const;
EdgeCandGroup timeExpand(const EdgeCand& ec, int time) const;
std::set<uint32_t> getColorMatch(const trgraph::Edge* e,
const RoutingAttrs& rAttrs) const;
void updateRouteColors(const RouteRefColors& c);
uint32_t getTextColor(uint32_t c) const;
void writeTransitGraph(const router::EdgeListHops& shp, TrGraphEdgs* edgs,
const std::vector<pfaedle::gtfs::Trip*>& trips) const;
void shapeWorker(
const std::vector<const TripForest*>* tries, std::atomic<size_t>* at,
std::map<std::string, size_t>* shpUsage,
std::map<Route*, std::map<uint32_t, std::vector<gtfs::Trip*>>>*,
TrGraphEdgs* gtfsGraph);
void edgCandWorker(std::vector<const Stop*>* stops, GrpCache* cache);
void clusterWorker(const std::vector<RoutingAttrs>* rAttrs,
const std::map<RoutingAttrs, std::vector<Trip*>>* trips,
TripForests* forest);
pfaedle::trgraph::EdgeGrid _eGrid;
pfaedle::trgraph::NodeGrid _nGrid;
};
} // namespace router
} // namespace pfaedle

View file

@ -0,0 +1,48 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_STATS_H_
#define PFAEDLE_ROUTER_STATS_H_
#include <algorithm>
#include <iostream>
#include <string>
#include "util/String.h"
namespace pfaedle {
namespace router {
struct Stats {
Stats()
: totNumTrips(0),
numTries(0),
numTrieLeafs(0),
solveTime(0),
dijkstraIters(0) {}
size_t totNumTrips;
size_t numTries;
size_t numTrieLeafs;
double solveTime;
size_t dijkstraIters;
};
inline Stats operator+ (const Stats& c1, const Stats& c2) {
Stats ret = c1;
ret.totNumTrips += c2.totNumTrips;
ret.numTries += c2.numTries;
ret.numTrieLeafs += c2.numTrieLeafs;
ret.solveTime += c2.solveTime;
ret.dijkstraIters += c2.dijkstraIters;
return ret;
}
inline Stats& operator+= (Stats& c1, const Stats& c2) {
c1 = c1 + c2;
return c1;
}
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_STATS_H_

View file

@ -0,0 +1,67 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_TRIPTRIE_H_
#define PFAEDLE_ROUTER_TRIPTRIE_H_
#include <vector>
#include <map>
#include <string>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/gtfs/StopTime.h"
#include "pfaedle/router/RoutingAttrs.h"
namespace pfaedle {
namespace router {
struct TripTrieNd {
const ad::cppgtfs::gtfs::Stop* reprStop;
std::string stopName; // the stop name at this node
std::string platform; // the platform of node
POINT pos; // the position of this node
double lat, lng;
int time;
bool arr;
int accTime;
size_t trips;
size_t parent;
std::vector<size_t> childs;
RoutingAttrs rAttrs;
};
template <typename TRIP>
class TripTrie {
public:
// init node 0, this is the first decision node
TripTrie() : _nds(1) {}
bool addTrip(TRIP* trip, const RoutingAttrs& rAttrs,
bool timeEx, bool degen);
const std::vector<TripTrieNd>& getNds() const;
const TripTrieNd& getNd(size_t nid) const;
void toDot(std::ostream& os, const std::string& rootName, size_t gid) const;
const std::map<size_t, std::vector<TRIP*>>& getNdTrips() const;
private:
std::vector<TripTrieNd> _nds;
std::map<TRIP*, size_t> _tripNds;
std::map<size_t, std::vector<TRIP*>> _ndTrips;
bool add(TRIP* trip, const RoutingAttrs& rAttrs, bool timeEx);
size_t get(TRIP* trip, bool timeEx);
size_t getMatchChild(size_t parentNid, const std::string& stopName,
const std::string& platform, POINT pos, int time,
bool timeEx) const;
size_t insert(const ad::cppgtfs::gtfs::Stop* stop, const RoutingAttrs& rAttrs,
const POINT& pos, int time, bool arr, size_t parent);
};
#include "pfaedle/router/TripTrie.tpp"
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_TRIPTRIE_H_

View file

@ -0,0 +1,246 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <map>
#include <string>
#include <vector>
#include "TripTrie.h"
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/gtfs/StopTime.h"
using pfaedle::gtfs::Trip;
using pfaedle::router::TripTrie;
// _____________________________________________________________________________
template <typename TRIP>
bool TripTrie<TRIP>::addTrip(TRIP* trip, const RoutingAttrs& rAttrs,
bool timeEx, bool degen) {
if (!degen) return add(trip, rAttrs, timeEx);
// check if trip is already fully and uniquely contained, if not, fail
size_t existing = get(trip, timeEx);
if (existing && _nds[existing].childs.size() == 0) {
_tripNds[trip] = existing;
_ndTrips[existing].push_back(trip);
return true;
} else {
return false;
}
}
// _____________________________________________________________________________
template <typename TRIP>
bool TripTrie<TRIP>::add(TRIP* trip, const RoutingAttrs& rAttrs, bool timeEx) {
if (trip->getStopTimes().size() == 0) return false;
int startSecs = 0;
if (!trip->getStopTimes().front().getDepartureTime().empty()) {
startSecs = trip->getStopTimes().front().getDepartureTime().seconds();
}
size_t curNdId = 0;
for (size_t stId = 0; stId < trip->getStopTimes().size(); stId++) {
const auto st = trip->getStopTimes()[stId];
std::string name = st.getStop()->getName();
std::string platform = st.getStop()->getPlatformCode();
POINT pos = util::geo::latLngToWebMerc<PFDL_PREC>(st.getStop()->getLat(),
st.getStop()->getLng());
if (stId > 0) {
int arrTime = startSecs;
if (!st.getArrivalTime().empty()) {
arrTime = st.getArrivalTime().seconds() - startSecs;
}
size_t arrChild =
getMatchChild(curNdId, name, platform, pos, arrTime, timeEx);
if (arrChild) {
curNdId = arrChild;
_nds[arrChild].accTime += arrTime;
_nds[arrChild].trips += 1;
_nds[arrChild].rAttrs.merge(rAttrs);
} else {
curNdId = insert(st.getStop(), rAttrs, pos, arrTime, true, curNdId);
}
}
if (stId < trip->getStopTimes().size() - 1) {
int depTime = startSecs;
if (!st.getDepartureTime().empty()) {
depTime = st.getDepartureTime().seconds() - startSecs;
}
size_t depChild =
getMatchChild(curNdId, name, platform, pos, depTime, timeEx);
if (depChild) {
curNdId = depChild;
_nds[depChild].accTime += depTime;
_nds[depChild].trips += 1;
_nds[depChild].rAttrs.merge(rAttrs);
} else {
if (stId == 0 && _tripNds.size() > 0) return false;
curNdId = insert(st.getStop(), rAttrs, pos, depTime, false, curNdId);
}
}
}
// curNdId is now the last matching node, insert the trip here
_tripNds[trip] = curNdId;
_ndTrips[curNdId].push_back(trip);
return true;
}
// _____________________________________________________________________________
template <typename TRIP>
size_t TripTrie<TRIP>::get(TRIP* trip, bool timeEx) {
if (trip->getStopTimes().size() == 0) return false;
int startSecs = trip->getStopTimes().front().getDepartureTime().seconds();
size_t curNdId = 0;
for (size_t stId = 0; stId < trip->getStopTimes().size(); stId++) {
const auto st = trip->getStopTimes()[stId];
std::string name = st.getStop()->getName();
std::string platform = st.getStop()->getPlatformCode();
POINT pos = util::geo::latLngToWebMerc<PFDL_PREC>(st.getStop()->getLat(),
st.getStop()->getLng());
if (stId > 0) {
int arrTime = startSecs;
if (!st.getArrivalTime().empty()) {
arrTime = st.getArrivalTime().seconds() - startSecs;
}
size_t arrChild =
getMatchChild(curNdId, name, platform, pos, arrTime, timeEx);
if (arrChild) {
curNdId = arrChild;
} else {
return 0;
}
}
if (stId < trip->getStopTimes().size() - 1) {
int depTime = startSecs;
if (!st.getDepartureTime().empty()) {
depTime = st.getDepartureTime().seconds() - startSecs;
}
size_t depChild =
getMatchChild(curNdId, name, platform, pos, depTime, timeEx);
if (depChild) {
curNdId = depChild;
} else {
return 0;
}
}
}
return curNdId;
}
// _____________________________________________________________________________
template <typename TRIP>
size_t TripTrie<TRIP>::insert(const ad::cppgtfs::gtfs::Stop* stop,
const RoutingAttrs& rAttrs, const POINT& pos,
int time, bool arr, size_t parent) {
_nds.emplace_back(TripTrieNd{stop,
stop->getName(),
stop->getPlatformCode(),
pos,
stop->getLat(),
stop->getLng(),
time,
arr,
time,
1,
parent,
{},
rAttrs});
_nds[parent].childs.push_back(_nds.size() - 1);
return _nds.size() - 1;
}
// _____________________________________________________________________________
template <typename TRIP>
const std::vector<pfaedle::router::TripTrieNd>& TripTrie<TRIP>::getNds() const {
return _nds;
}
// _____________________________________________________________________________
template <typename TRIP>
size_t TripTrie<TRIP>::getMatchChild(size_t parentNid,
const std::string& stopName,
const std::string& platform, POINT pos,
int time, bool timeEx) const {
for (size_t child : _nds[parentNid].childs) {
if (_nds[child].stopName == stopName && _nds[child].platform == platform &&
util::geo::dist(_nds[child].pos, pos) < 1 &&
(!timeEx || _nds[child].time == time)) {
return child;
}
}
return 0;
}
// _____________________________________________________________________________
template <typename TRIP>
void TripTrie<TRIP>::toDot(std::ostream& os, const std::string& rootName,
size_t gid) const {
os << "digraph triptrie" << gid << " {";
for (size_t nid = 0; nid < _nds.size(); nid++) {
std::string color = "white";
if (_ndTrips.count(nid)) color = "red";
if (nid == 0) {
os << "\"" << gid << ":0\" [label=\"" << rootName << "\"];\n";
} else {
os << "\"" << gid << ":" << nid
<< "\" [shape=\"box\" style=\"filled\" fillcolor=\"" << color
<< "\" label=\"#" << nid << ", " << _nds[nid].stopName << "@"
<< util::geo::getWKT(_nds[nid].pos) << " t=" << _nds[nid].time
<< "\"];\n";
}
}
for (size_t nid = 0; nid < _nds.size(); nid++) {
for (size_t child : _nds[nid].childs) {
os << "\"" << gid << ":" << nid << "\" -> \"" << gid << ":" << child
<< "\";\n";
}
}
os << "}";
}
// _____________________________________________________________________________
template <typename TRIP>
const std::map<size_t, std::vector<TRIP*>>& TripTrie<TRIP>::getNdTrips() const {
return _ndTrips;
}
// _____________________________________________________________________________
template <typename TRIP>
const pfaedle::router::TripTrieNd& TripTrie<TRIP>::getNd(size_t nid) const {
return _nds[nid];
}

View file

@ -0,0 +1,259 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <limits>
#include "pfaedle/router/Weights.h"
using pfaedle::router::DistDiffTransWeight;
using pfaedle::router::ExpoTransWeight;
using pfaedle::router::LineSimilarity;
using pfaedle::router::NormDistrTransWeight;
using util::geo::haversine;
// _____________________________________________________________________________
ExpoTransWeight::DistHeur::DistHeur(double maxV, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos)
: _rOpts(rOpts), _maxV(maxV), _maxCentD(0), _lastE(0) {
size_t c = 0;
double x = 0, y = 0;
for (const auto to : tos) {
x += to->getFrom()->pl().getGeom()->getX();
y += to->getFrom()->pl().getGeom()->getY();
c++;
}
x /= c;
y /= c;
_center = POINT{x, y};
for (const auto to : tos) {
const double cur = haversine(*to->getFrom()->pl().getGeom(), _center);
if (cur > _maxCentD) _maxCentD = cur;
}
_maxCentD /= _maxV;
}
// _____________________________________________________________________________
uint32_t ExpoTransWeight::DistHeur::operator()(
const trgraph::Edge* a, const std::set<trgraph::Edge*>& b) const {
UNUSED(b);
// avoid repeated calculation for the same edge over and over again
if (a == _lastE) return _lastC;
_lastE = a;
const double d = haversine(*a->getFrom()->pl().getGeom(), _center);
const double heur = fmax(0, (d / _maxV - _maxCentD) * 10);
// avoid overflow
if (heur > std::numeric_limits<uint32_t>::max()) {
_lastC = std::numeric_limits<uint32_t>::max();
;
return _lastC;
}
_lastC = heur;
return heur;
}
// _____________________________________________________________________________
uint32_t ExpoTransWeight::CostFunc::operator()(const trgraph::Edge* from,
const trgraph::Node* n,
const trgraph::Edge* to) const {
if (!from) return 0;
uint32_t c = from->pl().getCost();
if (c == std::numeric_limits<uint32_t>::max()) return c;
if (from == _lastFrom) {
// the transit line simi calculation is independent of the "to" edge, so if
// the last "from" edge was the same, skip it!
c = _lastC;
} else if (!_noLineSimiPen) {
const auto& simi = transitLineSimi(from);
if (!simi.nameSimilar) {
if (_rOpts.lineUnmatchedPunishFact < 1) {
c = std::ceil(static_cast<double>(c) * _rOpts.lineUnmatchedPunishFact);
} else if (_rOpts.lineUnmatchedPunishFact > 1) {
double a =
std::round(static_cast<double>(c) * _rOpts.lineUnmatchedPunishFact);
if (a > std::numeric_limits<uint32_t>::max())
return std::numeric_limits<uint32_t>::max();
c = a;
}
}
if (!simi.fromSimilar) {
if (_rOpts.lineNameFromUnmatchedPunishFact < 1) {
c = std::ceil(static_cast<double>(c) *
_rOpts.lineNameFromUnmatchedPunishFact);
} else if (_rOpts.lineNameFromUnmatchedPunishFact > 1) {
double a = std::round(static_cast<double>(c) *
_rOpts.lineNameFromUnmatchedPunishFact);
if (a > std::numeric_limits<uint32_t>::max())
return std::numeric_limits<uint32_t>::max();
c = a;
}
}
if (!simi.toSimilar) {
if (_rOpts.lineNameToUnmatchedPunishFact < 1) {
c = std::ceil(static_cast<double>(c) *
_rOpts.lineNameToUnmatchedPunishFact);
} else if (_rOpts.lineNameToUnmatchedPunishFact > 1) {
double a = std::round(static_cast<double>(c) *
_rOpts.lineNameToUnmatchedPunishFact);
if (a > std::numeric_limits<uint32_t>::max())
return std::numeric_limits<uint32_t>::max();
c = a;
}
}
_lastC = c;
_lastFrom = from;
}
uint32_t overflowCheck = c;
if (n && !n->pl().isTurnCycle()) {
if (_rOpts.fullTurnPunishFac != 0 && from->getFrom() == to->getTo() &&
from->getTo() == to->getFrom()) {
// trivial full turn
c += _rOpts.fullTurnPunishFac;
if (c <= overflowCheck) return std::numeric_limits<uint32_t>::max();
overflowCheck = c;
} else if (_rOpts.fullTurnPunishFac != 0 && n->getDeg() > 2) {
// otherwise, only intersection angles will be punished
double ang = util::geo::innerProd(
*n->pl().getGeom(), from->pl().backHop(), to->pl().frontHop());
if (ang < _rOpts.fullTurnAngle) {
c += _rOpts.fullTurnPunishFac;
if (c <= overflowCheck) return std::numeric_limits<uint32_t>::max();
overflowCheck = c;
}
}
// turn restriction cost
if (_rOpts.turnRestrCost > 0 && from->pl().isRestricted() &&
!_res.may(from, to, n)) {
c += _rOpts.turnRestrCost;
if (c <= overflowCheck) return std::numeric_limits<uint32_t>::max();
}
}
return c;
}
// _____________________________________________________________________________
LineSimilarity ExpoTransWeight::CostFunc::transitLineSimi(
const trgraph::Edge* e) const {
if (_rAttrs.shortName.empty() && _rAttrs.lineFrom.empty() &&
_rAttrs.lineTo.empty())
return {true, true, true};
LineSimilarity best = {false, false, false};
for (const auto* l : e->pl().getLines()) {
auto simi = _rAttrs.simi(l);
if (simi.nameSimilar && simi.toSimilar && simi.fromSimilar) return simi;
if (best < simi) best = simi;
}
return best;
}
// _____________________________________________________________________________
double ExpoTransWeight::weight(uint32_t c, double d, double t0, double d0,
const RoutingOpts& rOpts) {
UNUSED(t0);
UNUSED(d);
UNUSED(d0);
return rOpts.transitionPen * static_cast<double>(c) / 10.0;
}
// _____________________________________________________________________________
uint32_t ExpoTransWeight::invWeight(double c, const RoutingOpts& rOpts) {
return std::round((c / rOpts.transitionPen) * 10.0);
}
// _____________________________________________________________________________
uint32_t ExpoTransWeight::maxCost(double tTime, const RoutingOpts& rOpts) {
// abort after 3 times the scheduled time, but assume a min time of
// 1 minute!
return std::ceil(fmax(tTime, 60) * 3.0 * rOpts.lineUnmatchedPunishFact *
rOpts.lineNameToUnmatchedPunishFact *
rOpts.lineNameFromUnmatchedPunishFact * 10);
}
// _____________________________________________________________________________
// _____________________________________________________________________________
double NormDistrTransWeight::weight(uint32_t cs, double d, double t0, double d0,
const RoutingOpts& rOpts) {
UNUSED(d);
UNUSED(d0);
UNUSED(rOpts);
double t = static_cast<double>(cs) / 10.0;
// standard deviation of normal distribution
double standarddev = 1;
// no backwards time travel!
if (t0 < 0) return std::numeric_limits<double>::infinity();
// always assume it takes at least 10 seconds to travel
t0 = fmax(10, t0);
double cNorm = (t / t0 - 1) / standarddev;
double normWeight = cNorm * cNorm;
double expWeight = ExpoTransWeight::weight(cs, d, t0, d0, rOpts);
return normWeight + expWeight;
}
// _____________________________________________________________________________
uint32_t NormDistrTransWeight::invWeight(double c, const RoutingOpts& rOpts) {
UNUSED(rOpts);
UNUSED(c);
throw(std::runtime_error("Cannot apply inv weight to DistDiffTransWeight"));
}
// _____________________________________________________________________________
// _____________________________________________________________________________
double DistDiffTransWeight::weight(uint32_t c, double d, double t0, double d0,
const RoutingOpts& rOpts) {
UNUSED(t0);
UNUSED(c);
double w = fabs(d - d0);
return rOpts.transitionPen * w;
}
// _____________________________________________________________________________
uint32_t DistDiffTransWeight::invWeight(double c, const RoutingOpts& rOpts) {
UNUSED(rOpts);
UNUSED(c);
throw(std::runtime_error("Cannot apply inv weight to DistDiffTransWeight"));
}
// _____________________________________________________________________________
uint32_t DistDiffTransWeight::maxCost(double tTime, const RoutingOpts& rOpts) {
UNUSED(tTime);
UNUSED(rOpts);
return std::numeric_limits<uint32_t>::max();
}

View file

@ -0,0 +1,161 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_ROUTER_WEIGHTS_H_
#define PFAEDLE_ROUTER_WEIGHTS_H_
#include <set>
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/Misc.h"
#include "pfaedle/router/RoutingAttrs.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/graph/EDijkstra.h"
namespace pfaedle {
namespace router {
typedef util::graph::EDijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL,
uint32_t>
RCostFunc;
typedef util::graph::EDijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL,
uint32_t>
RHeurFunc;
class ExpoTransWeight {
public:
struct CostFunc : public RCostFunc {
CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& res, uint32_t max)
: _rAttrs(rAttrs),
_rOpts(rOpts),
_res(res),
_inf(max),
_noLineSimiPen(false),
_lastFrom(0) {
if (_rAttrs.lineFrom.empty() && _rAttrs.lineTo.empty() &&
_rAttrs.shortName.empty()) {
_noLineSimiPen = true;
}
if (_rOpts.lineUnmatchedPunishFact == 1) {
_noLineSimiPen = true;
}
}
const RoutingAttrs& _rAttrs;
const RoutingOpts& _rOpts;
const osm::Restrictor& _res;
uint32_t _inf;
bool _noLineSimiPen;
mutable const trgraph::Edge* _lastFrom;
mutable uint32_t _lastC = 0;
uint32_t operator()(const trgraph::Edge* from, const trgraph::Node* n,
const trgraph::Edge* to) const;
uint32_t inf() const { return _inf; }
LineSimilarity transitLineSimi(const trgraph::Edge* e) const;
};
struct DistHeur : RHeurFunc {
DistHeur(double maxV, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos);
const RoutingOpts& _rOpts;
double _maxV;
POINT _center;
double _maxCentD;
uint32_t operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const;
mutable const trgraph::Edge* _lastE;
mutable uint32_t _lastC = 0;
};
static uint32_t maxCost(double tTime, const RoutingOpts& rOpts);
static double weight(uint32_t c, double d, double t0, double d0,
const RoutingOpts& rOpts);
static uint32_t invWeight(double cost, const RoutingOpts& rOpts);
static const bool ALLOWS_FAST_ROUTE = true;
static const bool NEED_DIST = false;
};
class ExpoTransWeightNoHeur : public ExpoTransWeight {
public:
struct DistHeur : RHeurFunc {
DistHeur(double maxV, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos) {
UNUSED(maxV);
UNUSED(rOpts);
UNUSED(tos);
}
uint32_t operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const {
UNUSED(a);
UNUSED(b);
return 0;
}
};
};
class NormDistrTransWeight : public ExpoTransWeight {
public:
static double weight(uint32_t c, double d, double t0, double d0,
const RoutingOpts& rOpts);
static uint32_t invWeight(double cost, const RoutingOpts& rOpts);
static const bool ALLOWS_FAST_ROUTE = false;
static const bool NEED_DIST = false;
};
class NormDistrTransWeightNoHeur : public NormDistrTransWeight {
public:
struct DistHeur : RHeurFunc {
DistHeur(double maxV, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos) {
UNUSED(maxV);
UNUSED(rOpts);
UNUSED(tos);
}
uint32_t operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const {
UNUSED(a);
UNUSED(b);
return 0;
}
};
};
class DistDiffTransWeight : public ExpoTransWeight {
public:
static uint32_t maxCost(double tTime, const RoutingOpts& rOpts);
static double weight(uint32_t c, double d, double t0, double d0,
const RoutingOpts& rOpts);
static uint32_t invWeight(double cost, const RoutingOpts& rOpts);
static const bool ALLOWS_FAST_ROUTE = false;
static const bool NEED_DIST = true;
};
class DistDiffTransWeightNoHeur : public DistDiffTransWeight {
public:
struct DistHeur : RHeurFunc {
DistHeur(double maxV, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos) {
UNUSED(maxV);
UNUSED(rOpts);
UNUSED(tos);
}
uint32_t operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const {
UNUSED(a);
UNUSED(b);
return 0;
}
};
};
} // namespace router
} // namespace pfaedle
#endif // PFAEDLE_ROUTER_WEIGHTS_H_

View file

@ -0,0 +1,104 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <codecvt>
#include <exception>
#include <locale>
#include "pfaedle/Def.h"
#include "pfaedle/statsimi-classifier/StatsimiClassifier.h"
#include "util/geo/Geo.h"
using pfaedle::statsimiclassifier::BTSClassifier;
using pfaedle::statsimiclassifier::EDClassifier;
using pfaedle::statsimiclassifier::JaccardClassifier;
using pfaedle::statsimiclassifier::JaccardGeodistClassifier;
using pfaedle::statsimiclassifier::PEDClassifier;
// _____________________________________________________________________________
bool JaccardGeodistClassifier::similar(const std::string& nameA,
const POINT& posA,
const std::string& nameB,
const POINT& posB) const {
const double THRES_M =
0.00815467271246994481; // ln 2/85 from statsimi evaluation
const double THRES_JACC = .5; // from statsimi evaluation
const double m = exp(-THRES_M * util::geo::haversine(posA, posB));
double jacc = util::jaccardSimi(nameA, nameB);
if (jacc > THRES_JACC)
jacc = .5 + (jacc - THRES_JACC) / (2.0 * (1.0 - THRES_JACC));
else
jacc = jacc / (2.0 * THRES_JACC);
return ((m + jacc) / 2.0) > 0.5;
}
// _____________________________________________________________________________
bool JaccardGeodistClassifier::similar(const std::string& nameA,
const std::string& nameB) const {
return util::jaccardSimi(nameA, nameB) > 0.45; // 0.45 from statsimi paper
}
// _____________________________________________________________________________
bool JaccardClassifier::similar(const std::string& nameA, const POINT& posA,
const std::string& nameB,
const POINT& posB) const {
UNUSED(posA);
UNUSED(posB);
return similar(nameA, nameB);
}
// _____________________________________________________________________________
bool JaccardClassifier::similar(const std::string& nameA,
const std::string& nameB) const {
return util::jaccardSimi(nameA, nameB) > 0.45; // 0.45 from statsimi paper
}
// _____________________________________________________________________________
bool BTSClassifier::similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const {
UNUSED(posA);
UNUSED(posB);
return similar(nameA, nameB);
}
// _____________________________________________________________________________
bool BTSClassifier::similar(const std::string& nameA,
const std::string& nameB) const {
return util::btsSimi(nameA, nameB) > 0.85; // 0.85 from statsimi paper
}
// _____________________________________________________________________________
bool EDClassifier::similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const {
UNUSED(posA);
UNUSED(posB);
return similar(nameA, nameB);
}
// _____________________________________________________________________________
bool EDClassifier::similar(const std::string& nameA,
const std::string& nameB) const {
double edSimi = 1.0 - ((util::editDist(nameA, nameB) * 1.0) /
fmax(nameA.size(), nameB.size()));
return edSimi > 0.85; // 0.85 from statsimi paper
}
// _____________________________________________________________________________
bool PEDClassifier::similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const {
UNUSED(posA);
UNUSED(posB);
return similar(nameA, nameB);
}
// _____________________________________________________________________________
bool PEDClassifier::similar(const std::string& nameA,
const std::string& nameB) const {
double a = (util::prefixEditDist(nameA, nameB) * 1.0) / (nameA.size() * 1.0);
double b = (util::prefixEditDist(nameB, nameA) * 1.0) / (nameB.size() * 1.0);
double pedSimi = 1.0 - fmin(a, b);
return pedSimi > 0.875; // 0.875 average of values from statsimi paper
}

View file

@ -0,0 +1,68 @@
// Copyright 2020, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_
#define PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_
#include <string>
#include "pfaedle/Def.h"
#include "util/geo/Geo.h"
namespace pfaedle {
namespace statsimiclassifier {
class StatsimiClassifier {
public:
virtual ~StatsimiClassifier() {}
virtual bool similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const = 0;
virtual bool similar(const std::string& nameA,
const std::string& nameB) const = 0;
};
class JaccardClassifier : public StatsimiClassifier {
public:
virtual bool similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const;
virtual bool similar(const std::string& nameA,
const std::string& nameB) const;
};
class JaccardGeodistClassifier : public StatsimiClassifier {
public:
virtual bool similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const;
virtual bool similar(const std::string& nameA,
const std::string& nameB) const;
};
class BTSClassifier : public StatsimiClassifier {
public:
virtual bool similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const;
virtual bool similar(const std::string& nameA,
const std::string& nameB) const;
};
class EDClassifier : public StatsimiClassifier {
public:
virtual bool similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const;
virtual bool similar(const std::string& nameA,
const std::string& nameB) const;
};
class PEDClassifier : public StatsimiClassifier {
public:
virtual bool similar(const std::string& nameA, const POINT& posA,
const std::string& nameB, const POINT& posB) const;
virtual bool similar(const std::string& nameA,
const std::string& nameB) const;
};
} // namespace statsimiclassifier
} // namespace pfaedle
#endif // PFAEDLE_STATSIMI_CLASSIFIER_STATSIMICLASSIFIER_H_

View file

@ -0,0 +1,2 @@
add_executable(pfaedleTest TestMain.cpp)
target_link_libraries(pfaedleTest pfaedle_dep util)

View file

@ -0,0 +1,329 @@
// Copyright 2020
// Author: Patrick Brosi
#include "pfaedle/osm/Restrictor.h"
#define private public
#include "pfaedle/router/Router.h"
#undef private
#define private private
using pfaedle::osm::Restrictor;
using pfaedle::router::CostMatrix;
using pfaedle::router::EdgeCandGroup;
using pfaedle::router::ExpoTransWeight;
using pfaedle::router::LayerCostsDAG;
using pfaedle::router::RouterImpl;
using pfaedle::router::RoutingAttrs;
using pfaedle::router::RoutingOpts;
using util::approx;
// _____________________________________________________________________________
uint32_t cmGet(const CostMatrix& m, size_t i, size_t j) {
for (const auto& e : m) {
if (e.first.first == i && e.first.second == j) return e.second;
}
return -1;
}
// _____________________________________________________________________________
int main(int argc, char** argv) {
UNUSED(argc);
UNUSED(argv);
RouterImpl<ExpoTransWeight> router;
RoutingAttrs rAttrs;
RoutingOpts rOpts;
Restrictor restr;
LayerCostsDAG initCosts;
// to make sure we always underestimate the cost in the heuristic for testing
pfaedle::trgraph::NodePL::comps.emplace_back(
pfaedle::trgraph::Component{9999999});
// build transit graph
pfaedle::trgraph::Graph g;
auto a = g.addNd(POINT{0, 0});
auto b = g.addNd(POINT{0, 10});
auto c = g.addNd(POINT{10, 0});
auto d = g.addNd(POINT{20, 0});
a->pl().setComp(1);
b->pl().setComp(1);
c->pl().setComp(1);
d->pl().setComp(1);
auto eA = g.addEdg(a, c);
auto eB = g.addEdg(b, c);
auto eC = g.addEdg(c, d);
eA->pl().setCost(10);
eB->pl().setCost(6);
eC->pl().setCost(100);
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 0, {}, 0, {}});
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
TEST(cmGet(costM, 0, 0), ==, approx(10));
TEST(cmGet(costM, 1, 0), ==, approx(6));
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 0.5, {}, 0, {}});
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
TEST(cmGet(costM, 0, 0), ==, approx(50 + 10));
TEST(cmGet(costM, 1, 0), ==, approx(50 + 6));
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
tos.push_back({eC, 0, 0, {}, 0, {}});
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
TEST(cmGet(costM, 0, 0), ==, approx(5));
TEST(cmGet(costM, 1, 0), ==, approx(2));
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
tos.push_back({eC, 0, 0.9, {}, 0, {}});
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
TEST(cmGet(costM, 0, 0), ==, approx(90 + 5));
TEST(cmGet(costM, 1, 0), ==, approx(90 + 2));
}
// with hopsfast
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 0, {}, 0, {}});
LayerCostsDAG initCost{0, 0};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
TEST(cmGet(costM, 0, 0), >=, maxTime);
TEST(cmGet(costM, 1, 0), ==, approx(6));
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 0.5, {}, 0, {}});
LayerCostsDAG initCost{0, 0};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
TEST(cmGet(costM, 0, 0), >=, maxTime);
TEST(cmGet(costM, 1, 0), ==, approx(50 + 6));
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
tos.push_back({eC, 0, 0, {}, 0, {}});
LayerCostsDAG initCost{0, 0};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
TEST(cmGet(costM, 0, 0), >=, maxTime);
TEST(cmGet(costM, 1, 0), ==, approx(2));
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
tos.push_back({eC, 0, 0.9, {}, 0, {}});
LayerCostsDAG initCost{0, 0};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
TEST(cmGet(costM, 0, 0), >=, maxTime);
TEST(cmGet(costM, 1, 0), ==, approx(90 + 2));
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 0, {}, 0, {}});
LayerCostsDAG initCost{0, 0};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
TEST(cmGet(costM, 0, 0), ==, approx(5));
TEST(cmGet(costM, 1, 0), >=, maxTime);
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 0, {}, 0, {}});
LayerCostsDAG initCost{9999, 0};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
TEST(cmGet(costM, 0, 0), ==, approx(5));
TEST(cmGet(costM, 1, 0), >=, maxTime);
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eA, 0, 0, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 0, {}, 0, {}});
LayerCostsDAG initCost{6, 0, 20};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
// we also get this, because the edge is the same!
TEST(cmGet(costM, 0, 0), ==, approx(5));
TEST(cmGet(costM, 1, 0), ==, approx(10));
TEST(cmGet(costM, 2, 0), >=, maxTime);
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eA, 0, 0, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 1, {}, 0, {}});
LayerCostsDAG initCost{6, 0, 20};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
// we also get this, because the edge is the same!
TEST(cmGet(costM, 0, 0), ==, approx(5 + 100));
TEST(cmGet(costM, 1, 0), ==, approx(10 + 100));
TEST(cmGet(costM, 2, 0), >=, maxTime);
}
{
EdgeCandGroup froms, tos;
CostMatrix costM, dists;
froms.push_back({eA, 0, 0.5, {}, 0, {}});
froms.push_back({eA, 0, 0, {}, 0, {}});
froms.push_back({eB, 0, 0, {}, 0, {}});
tos.push_back({eC, 0, 1, {}, 0, {}});
tos.push_back({eC, 0, 0.5, {}, 0, {}});
LayerCostsDAG initCost{6, 0, 20};
double maxTime = 9999;
pfaedle::router::HopCache c;
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
maxTime);
// we also get this, because the edge is the same!
TEST(cmGet(costM, 0, 0), ==, approx(5 + 100));
TEST(cmGet(costM, 1, 0), ==, approx(10 + 100));
TEST(cmGet(costM, 0, 1), ==, approx(5 + 50));
TEST(cmGet(costM, 1, 1), ==, approx(10 + 50));
TEST(cmGet(costM, 2, 0), >=, maxTime);
TEST(cmGet(costM, 2, 1), >=, maxTime);
}
exit(0);
}

View file

@ -11,15 +11,12 @@
using pfaedle::trgraph::EdgePL;
using pfaedle::trgraph::TransitEdgeLine;
std::map<LINE*, size_t> EdgePL::_flines;
std::map<const TransitEdgeLine*, size_t> EdgePL::_tlines;
// _____________________________________________________________________________
EdgePL::EdgePL()
: _length(0), _oneWay(0), _hasRestr(false), _rev(false), _lvl(0) {
_l = new LINE();
_flines[_l] = 1;
: _oneWay(0), _hasRestr(false), _rev(false), _lvl(0), _cost(0), _l(0) {
}
// _____________________________________________________________________________
@ -27,17 +24,20 @@ EdgePL::EdgePL(const EdgePL& pl) : EdgePL(pl, false) {}
// _____________________________________________________________________________
EdgePL::EdgePL(const EdgePL& pl, bool geoflat)
: _length(pl._length),
_oneWay(pl._oneWay),
: _oneWay(pl._oneWay),
_hasRestr(pl._hasRestr),
_rev(pl._rev),
_lvl(pl._lvl) {
_lvl(pl._lvl),
_cost(pl._cost),
_l(0) {
if (pl._l) {
if (geoflat) {
_l = pl._l;
} else {
_l = new LINE(*pl._l);
}
_flines[_l]++;
}
for (auto l : pl._lines) addLine(l);
}
@ -75,16 +75,23 @@ EdgePL EdgePL::revCopy() const {
}
// _____________________________________________________________________________
void EdgePL::setLength(double d) { _length = d; }
double EdgePL::getLength() const {
double len = 0;
// _____________________________________________________________________________
double EdgePL::getLength() const { return _length; }
for (size_t i = 1; i < _l->size(); i++) {
len += haversine((*_l)[i-1], (*_l)[i]);
}
return len;
}
// _____________________________________________________________________________
void EdgePL::addLine(const TransitEdgeLine* l) {
if (std::find(_lines.begin(), _lines.end(), l) == _lines.end()) {
auto lb = std::lower_bound(_lines.begin(), _lines.end(), l);
if (lb == _lines.end() || *lb != l) {
_lines.reserve(_lines.size() + 1);
_lines.push_back(l);
lb = std::lower_bound(_lines.begin(), _lines.end(), l);
_lines.insert(lb, l);
if (_tlines.count(l))
_tlines[l]++;
else
@ -103,7 +110,13 @@ const std::vector<const TransitEdgeLine*>& EdgePL::getLines() const {
}
// _____________________________________________________________________________
void EdgePL::addPoint(const POINT& p) { _l->push_back(p); }
void EdgePL::addPoint(const POINT& p) {
if (!_l) {
_l = new LINE();
_flines[_l] = 1;
}
_l->push_back(p);
}
// _____________________________________________________________________________
const LINE* EdgePL::getGeom() const { return _l; }
@ -114,8 +127,9 @@ LINE* EdgePL::getGeom() { return _l; }
// _____________________________________________________________________________
util::json::Dict EdgePL::getAttrs() const {
util::json::Dict obj;
obj["m_length"] = std::to_string(_length);
obj["m_length"] = std::to_string(getLength());
obj["oneway"] = std::to_string(static_cast<int>(_oneWay));
obj["cost"] = std::to_string(static_cast<double>(_cost) / 10.0);
obj["level"] = std::to_string(_lvl);
obj["restriction"] = isRestricted() ? "yes" : "no";
@ -152,10 +166,10 @@ void EdgePL::setOneWay(uint8_t dir) { _oneWay = dir; }
void EdgePL::setOneWay() { _oneWay = 1; }
// _____________________________________________________________________________
void EdgePL::setLvl(uint8_t lvl) { _lvl = lvl; }
uint32_t EdgePL::getCost() const { return _cost; }
// _____________________________________________________________________________
uint8_t EdgePL::lvl() const { return _lvl; }
void EdgePL::setCost(uint32_t c) { _cost = c; }
// _____________________________________________________________________________
void EdgePL::setRev() { _rev = true; }

View file

@ -16,8 +16,6 @@
using util::geograph::GeoEdgePL;
namespace pfaedle {
namespace trgraph {
@ -28,14 +26,17 @@ struct TransitEdgeLine {
std::string fromStr;
std::string toStr;
std::string shortName;
uint32_t color;
};
inline bool operator==(const TransitEdgeLine& a, const TransitEdgeLine& b) {
// ignoring color here!
return a.fromStr == b.fromStr && a.toStr == b.toStr &&
a.shortName == b.shortName;
}
inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) {
// ignoring color here!
return a.fromStr < b.fromStr ||
(a.fromStr == b.fromStr && a.toStr < b.toStr) ||
(a.fromStr == b.fromStr && a.toStr == b.toStr &&
@ -65,14 +66,20 @@ class EdgePL {
// Return the length in meters stored for this edge payload
double getLength() const;
// Set the length in meters for this edge payload
void setLength(double d);
// Set this edge as a one way node, either in the default direction of
// the edge (no arg), or the direction specified in dir
void setOneWay();
void setOneWay(uint8_t dir);
void setLvl(uint8_t lvl) { assert(lvl < 9); _lvl = lvl; }
uint8_t lvl() const { return _lvl; }
// Return the cost for this edge payload
uint32_t getCost() const;
// Set the cost for this edge payload
void setCost(uint32_t d);
// Mark this payload' edge as having some restrictions
void setRestricted();
@ -85,12 +92,6 @@ class EdgePL {
// True if this edge is restricted
bool isRestricted() const;
// Set the level of this edge.
void setLvl(uint8_t lvl);
// Return the level of this edge.
uint8_t lvl() const;
// Return the one-way code stored for this edge.
uint8_t oneWay() const;
@ -115,11 +116,11 @@ class EdgePL {
EdgePL revCopy() const;
private:
float _length;
uint8_t _oneWay : 2;
bool _hasRestr : 1;
bool _rev : 1;
uint8_t _lvl : 3;
uint8_t _lvl: 4;
uint32_t _cost; // costs in 1/10th seconds
LINE* _l;

View file

@ -24,8 +24,8 @@ namespace trgraph {
typedef util::graph::Edge<NodePL, EdgePL> Edge;
typedef util::graph::Node<NodePL, EdgePL> Node;
typedef util::graph::DirGraph<NodePL, EdgePL> Graph;
typedef Grid<Node*, Point, PFAEDLE_PRECISION> NodeGrid;
typedef Grid<Edge*, Line, PFAEDLE_PRECISION> EdgeGrid;
typedef Grid<Node*, Point, PFDL_PREC> NodeGrid;
typedef Grid<Edge*, Line, PFDL_PREC> EdgeGrid;
} // namespace trgraph
} // namespace pfaedle

View file

@ -3,22 +3,19 @@
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <string>
#include <vector>
#include <limits>
#include <unordered_map>
#include "pfaedle/trgraph/NodePL.h"
#include "pfaedle/trgraph/StatGroup.h"
#include "pfaedle/trgraph/StatInfo.h"
#include "util/String.h"
using pfaedle::trgraph::StatInfo;
using pfaedle::trgraph::NodePL;
using pfaedle::trgraph::Component;
using pfaedle::trgraph::NodePL;
using pfaedle::trgraph::StatInfo;
// we use the adress of this dummy station info as a special value
// of this node, meaning "is a station block". Re-using the _si field here
// saves some memory
StatInfo NodePL::_blockerSI = StatInfo();
std::unordered_map<const Component*, size_t> NodePL::_comps;
std::vector<Component> NodePL::comps;
std::vector<StatInfo> NodePL::_statInfos;
// _____________________________________________________________________________
NodePL::NodePL()
@ -32,19 +29,6 @@ NodePL::NodePL()
{
}
// _____________________________________________________________________________
NodePL::NodePL(const NodePL& pl)
: _geom(pl._geom),
_si(0),
_component(pl._component)
#ifdef PFAEDLE_DBG
,
_vis(pl._vis)
#endif
{
if (pl._si) setSI(*(pl._si));
}
// _____________________________________________________________________________
NodePL::NodePL(const POINT& geom)
: _geom(geom),
@ -70,18 +54,6 @@ NodePL::NodePL(const POINT& geom, const StatInfo& si)
setSI(si);
}
// _____________________________________________________________________________
NodePL::~NodePL() {
if (getSI()) delete _si;
if (_component) {
_comps[_component]--;
if (_comps[_component] == 0) {
delete _component;
_comps.erase(_comps.find(_component));
}
}
}
// _____________________________________________________________________________
void NodePL::setVisited() const {
#ifdef PFAEDLE_DBG
@ -93,18 +65,14 @@ void NodePL::setVisited() const {
void NodePL::setNoStat() { _si = 0; }
// _____________________________________________________________________________
const Component* NodePL::getComp() const { return _component; }
const Component& NodePL::getComp() const { return comps[_component - 1]; }
// _____________________________________________________________________________
void NodePL::setComp(const Component* c) {
if (_component == c) return;
_component = c;
uint32_t NodePL::getCompId() const { return _component; }
// NOT thread safe!
if (!_comps.count(c))
_comps[c] = 1;
else
_comps[c]++;
// _____________________________________________________________________________
void NodePL::setComp(uint32_t id) {
_component = id;
}
// _____________________________________________________________________________
@ -116,54 +84,59 @@ void NodePL::setGeom(const POINT& geom) { _geom = geom; }
// _____________________________________________________________________________
util::json::Dict NodePL::getAttrs() const {
util::json::Dict obj;
obj["component"] = std::to_string(reinterpret_cast<size_t>(_component));
obj["component"] = std::to_string(_component);
#ifdef PFAEDLE_DBG
obj["dijkstra_vis"] = _vis ? "yes" : "no";
#endif
if (getSI()) {
obj["station_info_ptr"] = util::toString(_si);
obj["station_name"] = _si->getName();
obj["station_alt_names"] = util::implode(_si->getAltNames(), ",");
obj["from_osm"] = _si->isFromOsm() ? "yes" : "no";
obj["station_platform"] = _si->getTrack();
obj["station_group"] =
std::to_string(reinterpret_cast<size_t>(_si->getGroup()));
obj["station_name"] = getSI()->getName();
obj["station_alt_names"] =
util::implode(getSI()->getAltNames(), ",");
obj["station_platform"] = getSI()->getTrack();
#ifdef PFAEDLE_STATION_IDS
// only print this in debug mode
obj["station_id"] = _si->getId();
obj["station_id"] = getSI()->getId();
#endif
std::stringstream gtfsIds;
if (_si->getGroup()) {
for (auto* s : _si->getGroup()->getStops()) {
gtfsIds << s->getId() << " (" << s->getName() << "),";
}
}
obj["station_group_stops"] = gtfsIds.str();
}
return obj;
}
// _____________________________________________________________________________
void NodePL::setSI(const StatInfo& si) { _si = new StatInfo(si); }
void NodePL::setSI(const StatInfo& si) {
_statInfos.emplace_back(si);
_si = _statInfos.size();
}
// _____________________________________________________________________________
const StatInfo* NodePL::getSI() const {
if (isBlocker()) return 0;
return _si;
if (isTurnCycle()) return 0;
if (_si == 0) return 0;
return &_statInfos[_si - 1];
}
// _____________________________________________________________________________
StatInfo* NodePL::getSI() {
if (isBlocker()) return 0;
return _si;
if (isTurnCycle()) return 0;
if (_si == 0) return 0;
return &_statInfos[_si - 1];
}
// _____________________________________________________________________________
void NodePL::setBlocker() { _si = &_blockerSI; }
void NodePL::setTurnCycle() { _si = std::numeric_limits<uint32_t>::max() - 1; }
// _____________________________________________________________________________
bool NodePL::isBlocker() const { return _si == &_blockerSI; }
bool NodePL::isTurnCycle() const {
return _si == (std::numeric_limits<uint32_t>::max() - 1);
}
// _____________________________________________________________________________
void NodePL::setBlocker() { _si = std::numeric_limits<uint32_t>::max(); }
// _____________________________________________________________________________
bool NodePL::isBlocker() const {
return _si == std::numeric_limits<uint32_t>::max();
}

View file

@ -8,6 +8,7 @@
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "pfaedle/trgraph/StatInfo.h"
@ -20,7 +21,7 @@ namespace pfaedle {
namespace trgraph {
struct Component {
uint8_t minEdgeLvl : 3;
float maxSpeed;
};
/*
@ -29,10 +30,8 @@ struct Component {
class NodePL {
public:
NodePL();
NodePL(const NodePL& pl); // NOLINT
NodePL(const POINT& geom); // NOLINT
NodePL(const POINT& geom, const StatInfo& si);
~NodePL();
// Return the geometry of this node.
const POINT* getGeom() const;
@ -52,10 +51,13 @@ class NodePL {
void setNoStat();
// Get the component of this node
const Component* getComp() const;
const Component& getComp() const;
// Get the component of this node
uint32_t getCompId() const;
// Set the component of this node
void setComp(const Component* c);
void setComp(uint32_t c);
// Make this node a blocker
void setBlocker();
@ -63,21 +65,27 @@ class NodePL {
// Check if this node is a blocker
bool isBlocker() const;
// Make this node a turning cycle
void setTurnCycle();
// Check if this node is a blocker
bool isTurnCycle() const;
// Mark this node as visited (usefull for counting search space in Dijkstra)
// (only works for DEBUG build type)
void setVisited() const;
static std::vector<Component> comps;
private:
POINT _geom;
StatInfo* _si;
const Component* _component;
uint32_t _si;
uint32_t _component;
#ifdef PFAEDLE_DBG
mutable bool _vis;
#endif
static StatInfo _blockerSI;
static std::unordered_map<const Component*, size_t> _comps;
static std::vector<StatInfo> _statInfos;
};
} // namespace trgraph
} // namespace pfaedle

View file

@ -3,6 +3,7 @@
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <algorithm>
#include <cassert>
#include <iostream>
#include <regex>
#include <sstream>
@ -15,12 +16,28 @@
using pfaedle::trgraph::Normalizer;
// _____________________________________________________________________________
Normalizer::Normalizer(const ReplRules& rules) : _rulesOrig(rules) {
Normalizer::Normalizer(const ReplRules& rules)
: _rulesOrig(rules) {
buildRules(rules);
}
// _____________________________________________________________________________
std::string Normalizer::operator()(std::string sn) const {
Normalizer::Normalizer(const Normalizer& other)
: _rules(other._rules),
_rulesOrig(other._rulesOrig),
_cache(other._cache) {}
// _____________________________________________________________________________
Normalizer& Normalizer::operator=(Normalizer other) {
std::swap(this->_rules, other._rules);
std::swap(this->_rulesOrig, other._rulesOrig);
std::swap(this->_cache, other._cache);
return *this;
}
// _____________________________________________________________________________
std::string Normalizer::norm(const std::string& sn) const {
auto i = _cache.find(sn);
if (i != _cache.end()) return i->second;

View file

@ -28,8 +28,15 @@ class Normalizer {
Normalizer() {}
explicit Normalizer(const ReplRules& rules);
// Normalize sn based on the rules of this normalizer
std::string operator()(std::string sn) const;
// copy constructor
Normalizer(const Normalizer& other);
// assignment op
Normalizer& operator=(Normalizer other);
// Normalize sn, not thread safe
std::string norm(const std::string& sn) const;
bool operator==(const Normalizer& b) const;
private:

View file

@ -1,94 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <set>
#include "pfaedle/trgraph/StatGroup.h"
#include "util/geo/Geo.h"
using pfaedle::trgraph::StatGroup;
using pfaedle::trgraph::Node;
using pfaedle::router::NodeCandGroup;
using ad::cppgtfs::gtfs::Stop;
// _____________________________________________________________________________
StatGroup::StatGroup() {}
// _____________________________________________________________________________
void StatGroup::addStop(const Stop* s) { _stops.insert(s); }
// _____________________________________________________________________________
void StatGroup::addNode(trgraph::Node* n) { _nodes.insert(n); }
// _____________________________________________________________________________
void StatGroup::merge(StatGroup* other) {
if (other == this) return;
std::set<Node*> nds = other->getNodes();
std::set<const Stop*> stops = other->getStops();
for (auto on : nds) {
on->pl().getSI()->setGroup(this);
addNode(on);
}
for (auto* os : stops) {
addStop(os);
}
}
// _____________________________________________________________________________
const NodeCandGroup& StatGroup::getNodeCands(const Stop* s) const {
return _stopNodePens.at(s);
}
// _____________________________________________________________________________
const std::set<Node*>& StatGroup::getNodes() const { return _nodes; }
// _____________________________________________________________________________
void StatGroup::remNode(trgraph::Node* n) {
auto it = _nodes.find(n);
if (it != _nodes.end()) _nodes.erase(it);
}
// _____________________________________________________________________________
std::set<Node*>& StatGroup::getNodes() { return _nodes; }
// _____________________________________________________________________________
const std::set<const Stop*>& StatGroup::getStops() const { return _stops; }
// _____________________________________________________________________________
double StatGroup::getPen(const Stop* s, trgraph::Node* n,
const trgraph::Normalizer& platformNorm,
double trackPen, double distPenFac,
double nonOsmPen) const {
POINT p =
util::geo::latLngToWebMerc<PFAEDLE_PRECISION>(s->getLat(), s->getLng());
double distPen = util::geo::webMercMeterDist(p, *n->pl().getGeom());
distPen *= distPenFac;
std::string platform = platformNorm(s->getPlatformCode());
if (!platform.empty() && !n->pl().getSI()->getTrack().empty() &&
n->pl().getSI()->getTrack() == platform) {
trackPen = 0;
}
if (n->pl().getSI()->isFromOsm()) nonOsmPen = 0;
return distPen + trackPen + nonOsmPen;
}
// _____________________________________________________________________________
void StatGroup::writePens(const trgraph::Normalizer& platformNorm,
double trackPen, double distPenFac,
double nonOsmPen) {
if (_stopNodePens.size()) return; // already written
for (auto* s : _stops) {
for (auto* n : _nodes) {
_stopNodePens[s].push_back(router::NodeCand{
n, getPen(s, n, platformNorm, trackPen, distPenFac, nonOsmPen)});
}
}
}

View file

@ -1,72 +0,0 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_TRGRAPH_STATGROUP_H_
#define PFAEDLE_TRGRAPH_STATGROUP_H_
#include <string>
#include <unordered_map>
#include <set>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/router/Router.h"
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/trgraph/Normalizer.h"
namespace pfaedle {
namespace trgraph {
using ad::cppgtfs::gtfs::Stop;
/*
* A group of stations that belong together semantically (for example, multiple
* stop points of a larger bus station)
*/
class StatGroup {
public:
StatGroup();
StatGroup(const StatGroup& a) = delete;
// Add a stop s to this station group
void addStop(const Stop* s);
// Add a node n to this station group
void addNode(trgraph::Node* n);
// Return all nodes contained in this group
const std::set<trgraph::Node*>& getNodes() const;
std::set<trgraph::Node*>& getNodes();
// Return all stops contained in this group
const std::set<const Stop*>& getStops() const;
// Remove a node from this group
void remNode(trgraph::Node* n);
// All nodes in other will be in this group, their SI's updated, and the
// "other" group deleted.
void merge(StatGroup* other);
// Return node candidates for stop s from this group
const router::NodeCandGroup& getNodeCands(const Stop* s) const;
// Write the penalties for all stops contained in this group so far.
void writePens(const trgraph::Normalizer& platformNorm, double trackPen,
double distPenFac, double nonOsmPen);
private:
std::set<trgraph::Node*> _nodes;
std::set<const Stop*> _stops;
// for each stop in this group, a penalty for each of the nodes here, based on
// its distance and optionally the track number
std::unordered_map<const Stop*, router::NodeCandGroup> _stopNodePens;
double getPen(const Stop* s, trgraph::Node* n,
const trgraph::Normalizer& norm, double trackPen,
double distPenFac, double nonOsmPen) const;
};
} // namespace trgraph
} // namespace pfaedle
#endif // PFAEDLE_TRGRAPH_STATGROUP_H_

View file

@ -3,66 +3,24 @@
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include "pfaedle/router/Comp.h"
#include "pfaedle/trgraph/StatGroup.h"
#include "pfaedle/trgraph/StatInfo.h"
using pfaedle::trgraph::StatInfo;
using pfaedle::trgraph::StatGroup;
std::unordered_map<const StatGroup*, size_t> StatInfo::_groups;
// _____________________________________________________________________________
StatInfo::StatInfo() : _name(""), _track(""), _fromOsm(false), _group(0) {}
StatInfo::StatInfo() : _name(""), _track("") {}
// _____________________________________________________________________________
StatInfo::StatInfo(const StatInfo& si)
: _name(si._name),
_altNames(si._altNames),
_track(si._track),
_fromOsm(si._fromOsm),
_group(0) {
setGroup(si._group);
: _name(si._name), _altNames(si._altNames), _track(si._track) {
#ifdef PFAEDLE_STATION_IDS
_id = si._id;
#endif
}
// _____________________________________________________________________________
StatInfo::StatInfo(const std::string& name, const std::string& track,
bool fromOsm)
: _name(name), _track(track), _fromOsm(fromOsm), _group(0) {}
// _____________________________________________________________________________
StatInfo::~StatInfo() { unRefGroup(_group); }
// _____________________________________________________________________________
void StatInfo::unRefGroup(StatGroup* g) {
if (g) {
_groups[g]--;
if (_groups[g] == 0) {
// std::cout << "Deleting " << g << std::endl;
delete g;
_groups.erase(_groups.find(g));
}
}
}
// _____________________________________________________________________________
void StatInfo::setGroup(StatGroup* g) {
if (_group == g) return;
unRefGroup(_group);
_group = g;
// NOT thread safe!
if (!_groups.count(g))
_groups[g] = 1;
else
_groups[g]++;
}
// _____________________________________________________________________________
StatGroup* StatInfo::getGroup() const { return _group; }
StatInfo::StatInfo(const std::string& name, const std::string& track)
: _name(name), _track(track) {}
// _____________________________________________________________________________
const std::string& StatInfo::getName() const { return _name; }
@ -70,12 +28,6 @@ const std::string& StatInfo::getName() const { return _name; }
// _____________________________________________________________________________
const std::string& StatInfo::getTrack() const { return _track; }
// _____________________________________________________________________________
bool StatInfo::isFromOsm() const { return _fromOsm; }
// _____________________________________________________________________________
void StatInfo::setIsFromOsm(bool is) { _fromOsm = is; }
// _____________________________________________________________________________
double StatInfo::simi(const StatInfo* other) const {
if (!other) return 0;

View file

@ -6,24 +6,20 @@
#define PFAEDLE_TRGRAPH_STATINFO_H_
#include <string>
#include <vector>
#include <unordered_map>
#include <vector>
namespace pfaedle {
namespace trgraph {
// forward declaration
class StatGroup;
/*
* Meta information (name, alternative names, track, group...) of a single stop
* Meta information (name, alternative names, track, ...) of a single stop
*/
class StatInfo {
public:
StatInfo();
StatInfo(const StatInfo& si);
StatInfo(const std::string& name, const std::string& track, bool _fromOsm);
~StatInfo();
StatInfo(const std::string& name, const std::string& track);
// Return this stops names.
const std::string& getName() const;
@ -43,18 +39,6 @@ class StatInfo {
// Return the similarity between this stop and other
double simi(const StatInfo* other) const;
// Set this stations group.
void setGroup(StatGroup* g);
// Return this stations group.
StatGroup* getGroup() const;
// True if this stop was from osm
bool isFromOsm() const;
// Set this stop as coming from osm
void setIsFromOsm(bool is);
#ifdef PFAEDLE_STATION_IDS
const std::string& getId() const { return _id; }
void setId(const std::string& id) { _id = id; }
@ -64,17 +48,12 @@ class StatInfo {
std::string _name;
std::vector<std::string> _altNames;
std::string _track;
bool _fromOsm;
StatGroup* _group;
#ifdef PFAEDLE_STATION_IDS
// debug feature to store station ids from both OSM
// and GTFS
std::string _id;
#endif
static std::unordered_map<const StatGroup*, size_t> _groups;
static void unRefGroup(StatGroup* g);
};
} // namespace trgraph
} // namespace pfaedle

View file

@ -0,0 +1,17 @@
file(GLOB_RECURSE shapevl_SRC *.cpp)
set(shapevl_main ShapevlMain.cpp)
list(REMOVE_ITEM shapevl_SRC ${shapevl_main})
include_directories(
${PFAEDLE_INCLUDE_DIR}
SYSTEM ${LIBZIP_INCLUDE_DIR}
SYSTEM ${LIBZIP_CONF_INCLUDE_DIR}
)
add_executable(shapevl ${shapevl_main})
add_library(shapevl_dep ${shapevl_SRC})
include_directories(shapevl_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src)
target_link_libraries(shapevl shapevl_dep util ad_cppgtfs -lpthread ${LIBZIP_LIBRARY})

485
src/shapevl/Collector.cpp Normal file
View file

@ -0,0 +1,485 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <fstream>
#include <set>
#include <string>
#include <utility>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "shapevl/Collector.h"
#include "shapevl/Result.h"
#include "util/geo/Geo.h"
#include "util/geo/PolyLine.h"
#include "util/geo/output/GeoJsonOutput.h"
#include "util/log/Log.h"
using util::geo::PolyLine;
using ad::cppgtfs::gtfs::Shape;
using ad::cppgtfs::gtfs::Trip;
using pfaedle::eval::Collector;
using pfaedle::eval::Result;
using util::geo::output::GeoJsonOutput;
// _____________________________________________________________________________
double Collector::add(const Trip* oldT, const Shape* oldS, const Trip* newT,
const Shape* newS) {
// This adds a new trip with a new shape to our evaluation.
_trips++;
if (!oldS) {
// If there is no original shape, we cannot compare them - abort!
_noOrigShp++;
return 0;
}
for (auto st : oldT->getStopTimes()) {
if (st.getShapeDistanceTravelled() < 0) {
// we cannot safely compare trips without shape dist travelled
// information - abort!
_noOrigShp++;
return 0;
}
}
for (auto st : newT->getStopTimes()) {
if (st.getShapeDistanceTravelled() < 0) {
// we cannot safely compare trips without shape dist travelled
// information - abort!
_noOrigShp++;
return 0;
}
}
double fd = 0;
// A "segment" is a path from station s_i to station s_{i+1}
size_t unmatchedSegments; // number of unmatched segments
double unmatchedSegmentsLength; // total _an. length of unmatched segments
std::vector<double> oldDists;
LINE oldL = getLine(oldS, &oldDists);
std::vector<double> newDists;
LINE newL = getLine(newS, &newDists);
// check dist between anchor points
if ((util::geo::latLngLen(oldL) * 1.0) / (oldL.size() * 1.0) > 1000) {
// most likely input with a degenerated shape - dont compare
_noOrigShp++;
return 0;
}
if ((util::geo::latLngLen(newL) * 1.0) / (newL.size() * 1.0) > 1000) {
// most likely input with a degenerated shape - dont compare
_noOrigShp++;
return 0;
}
std::vector<std::pair<double, double>> newLenDists;
std::vector<std::pair<double, double>> oldLenDists;
auto oldSegs = segmentize(oldT, oldL, oldDists, newLenDists);
auto newSegs = segmentize(newT, newL, newDists, oldLenDists);
for (const auto& p : oldLenDists) {
_distDiffs.push_back(fabs(p.first - p.second));
_hopDists.push_back(p.first);
}
// new lines build from cleaned-up shapes
LINE oldLCut;
LINE newLCut;
for (auto oldL : oldSegs)
oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end());
for (auto newL : newSegs) {
newLCut.insert(newLCut.end(), newL.begin(), newL.end());
}
// convert (roughly) to degrees
double SEGL = 25.0 / util::geo::M_PER_DEG;
double f = util::geo::webMercDistFactor(oldLCut.front());
// roughly half a meter
auto oldLCutS =
util::geo::simplify(oldLCut, f * (0.5 / util::geo::M_PER_DEG));
auto newLCutS =
util::geo::simplify(newLCut, f * (0.5 / util::geo::M_PER_DEG));
auto old = _dCache.find(oldLCutS);
if (old != _dCache.end()) {
auto match = old->second.find(newLCutS);
if (match != old->second.end()) {
fd = match->second;
} else {
fd = util::geo::accFrechetDistCHav(oldLCutS, newLCutS, SEGL);
_dCache[oldLCutS][newLCutS] = fd;
}
} else {
fd = util::geo::accFrechetDistCHav(oldLCutS, newLCutS, SEGL);
_dCache[oldLCutS][newLCutS] = fd;
}
auto dA = getDa(oldSegs, newSegs);
unmatchedSegments = dA.first;
unmatchedSegmentsLength = dA.second;
double totL = 0;
for (auto l : oldSegs) totL += util::geo::latLngLen(l);
// filter out shapes with a length of under 5 meters - they are most likely
// artifacts
if (totL < 5) {
_noOrigShp++;
return 0;
}
_fdSum += fd / totL;
_unmatchedSegSum += unmatchedSegments;
_unmatchedSegLengthSum += unmatchedSegmentsLength;
double avgFd = fd / totL;
double AN = static_cast<double>(unmatchedSegments) /
static_cast<double>(oldSegs.size());
double AL = unmatchedSegmentsLength / totL;
_results.insert(Result(oldT, avgFd));
if (AN <= 0.0001) _an0++;
if (AN <= 0.05) _an5++;
if (AN <= 0.1) _an10++;
if (AN <= 0.2) _an20++;
if (AN <= 0.3) _an30++;
if (AN <= 0.5) _an50++;
if (AN <= 0.7) _an70++;
if (AN <= 0.9) _an90++;
LOG(VDEBUG) << "This result (" << oldT->getId()
<< "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size()
<< " = " << AN << " A_L/L = " << unmatchedSegmentsLength << "/"
<< totL << " = " << AL << " d_f = " << avgFd;
if (_reportOut) {
(*_reportOut) << std::fixed << std::setprecision(6);
(*_reportOut) << oldT->getId() << "\t" << AN << "\t" << AL << "\t" << avgFd
<< "\t" << util::geo::getWKT(oldSegs) << "\t"
<< util::geo::getWKT(newSegs) << "\t" << oldT->getRoute()->getShortName() << "\t";
for (const auto& st : oldT->getStopTimes()) {
(*_reportOut) << st.getStop()->getName() << "\t"
<< st.getStop()->getLat() << "\t"
<< st.getStop()->getLng() << "\t";
}
(*_reportOut) << "\n";
}
return avgFd;
}
// _____________________________________________________________________________
std::vector<LINE> Collector::segmentize(
const Trip* t, const LINE& shape, const std::vector<double>& dists,
std::vector<std::pair<double, double>>& lenDist) {
std::vector<LINE> ret;
if (t->getStopTimes().size() < 2) return ret;
POLYLINE pl(shape);
std::vector<double> cuts;
size_t i = 0;
for (const auto& st : t->getStopTimes()) {
cuts.push_back(st.getShapeDistanceTravelled());
i++;
}
size_t to = std::upper_bound(dists.begin(), dists.end(), cuts[0]) -
dists.begin();
POINT lastP;
if (to >= dists.size()) {
lastP = shape.back();
} else if (to == 0) {
lastP = shape.front();
} else {
double progr = (cuts[0] - dists[to - 1]) / (dists[to] - dists[to - 1]);
lastP = shape[to - 1];
lastP.setX(lastP.getX() + progr * (shape[to].getX() - shape[to-1].getX()));
lastP.setY(lastP.getY() + progr * (shape[to].getY() - shape[to-1].getY()));
}
for (size_t i = 1; i < cuts.size(); i++) {
size_t to = std::upper_bound(dists.begin(), dists.end(), cuts[i]) -
dists.begin();
POINT curP;
if (to >= dists.size()) {
curP = shape.back();
} else if (to == 0) {
curP = shape.front();
} else {
curP = shape[to - 1];
double progr = (cuts[i] - dists[to - 1]) / (dists[to] - dists[to - 1]);
curP.setX(curP.getX() + progr * (shape[to].getX() - shape[to-1].getX()));
curP.setY(curP.getY() + progr * (shape[to].getY() - shape[to-1].getY()));
}
auto curL = pl.getSegment(lastP, curP).getLine();
double dist =
util::geo::haversine(t->getStopTimes()[i - 1].getStop()->getLat(),
t->getStopTimes()[i - 1].getStop()->getLng(),
t->getStopTimes()[i].getStop()->getLat(),
t->getStopTimes()[i].getStop()->getLng());
double len = util::geo::latLngLen(curL);
lenDist.push_back({dist, len});
ret.push_back(curL);
lastP = curP;
}
return ret;
}
// _____________________________________________________________________________
LINE Collector::getLine(const Shape* s, std::vector<double>* dists) {
LINE ret;
for (size_t i = 0; i < s->getPoints().size(); i++) {
ret.push_back({s->getPoints()[i].lng, s->getPoints()[i].lat});
(*dists).push_back(s->getPoints()[i].travelDist);
}
return ret;
}
// _____________________________________________________________________________
const std::set<Result>& Collector::getResults() const { return _results; }
// _____________________________________________________________________________
double Collector::getAvgDist() const { return _fdSum / _results.size(); }
// _____________________________________________________________________________
void Collector::printCsv(std::ostream* os,
const std::set<Result>& result) const {
for (auto r : result) (*os) << r.getDist() << "\n";
}
// _____________________________________________________________________________
double Collector::getAcc() const {
return static_cast<double>(_an0) / static_cast<double>(_results.size());
}
// _____________________________________________________________________________
void Collector::printShortStats(std::ostream* os) const {
if (_results.size()) {
(*os) << (static_cast<double>(_an0) /
static_cast<double>(_results.size())) *
100
<< ",";
(*os) << (static_cast<double>(_an5) /
static_cast<double>(_results.size())) *
100
<< ",";
(*os) << (static_cast<double>(_an10) /
static_cast<double>(_results.size())) *
100
<< ",";
(*os) << (static_cast<double>(_an20) /
static_cast<double>(_results.size())) *
100
<< ",";
(*os) << (static_cast<double>(_an30) /
static_cast<double>(_results.size())) *
100
<< ",";
(*os) << (static_cast<double>(_an50) /
static_cast<double>(_results.size())) *
100
<< ",";
(*os) << (static_cast<double>(_an70) /
static_cast<double>(_results.size())) *
100
<< ",";
(*os) << (static_cast<double>(_an90) /
static_cast<double>(_results.size())) *
100;
}
}
// _____________________________________________________________________________
void Collector::printStats(std::ostream* os) const {
(*os) << std::setfill(' ') << std::setw(50) << " # of trips: " << _trips
<< "\n";
(*os) << std::setfill(' ') << std::setw(50)
<< " # of trips new shapes were matched for: " << _results.size()
<< "\n";
(*os) << std::setw(50) << " # of trips without input shapes: " << _noOrigShp
<< "\n";
if (_results.size()) {
(*os) << std::setw(50) << " highest avg frechet distance to input shapes: "
<< (--_results.end())->getDist() << " (on trip #"
<< (--_results.end())->getTrip()->getId() << ")\n";
(*os) << std::setw(50) << " lowest distance to input shapes: "
<< (_results.begin())->getDist() << " (on trip #"
<< (_results.begin())->getTrip()->getId() << ")\n";
(*os) << std::setw(50)
<< " averaged avg frechet distance: " << getAvgDist() << "\n";
(*os) << "\n";
(*os) << " an-0: "
<< (static_cast<double>(_an0) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
(*os) << " an-5: "
<< (static_cast<double>(_an5) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
(*os) << " an-10: "
<< (static_cast<double>(_an10) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
(*os) << " an-20: "
<< (static_cast<double>(_an20) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
(*os) << " acc-30: "
<< (static_cast<double>(_an30) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
(*os) << " acc-50: "
<< (static_cast<double>(_an50) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
(*os) << " acc-70: "
<< (static_cast<double>(_an70) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
(*os) << " acc-90: "
<< (static_cast<double>(_an90) /
static_cast<double>(_results.size())) *
100
<< " %"
<< "\n";
}
(*os) << std::endl;
}
// _____________________________________________________________________________
std::map<string, double> Collector::getStats() {
std::map<string, double> stats;
if (_distDiffs.size()) {
auto i = _distDiffs.begin() + _distDiffs.size() / 2;
// std::nth_element makes a partial sort of the first n elements
std::nth_element(_distDiffs.begin(), i, _distDiffs.end());
stats["median-dist-diff"] = *i;
} else {
stats["median-dist-diff"] = -1;
}
if (_hopDists.size()) {
double s = 0;
for (auto d : _hopDists) s += d;
stats["avg-hop-dist"] = s / (_hopDists.size() * 1.0);
} else {
stats["avg-hop-dist"] = -1;
}
stats["num-trips"] = _trips;
stats["num-trips-matched"] = _results.size();
stats["num-trips-wo-shapes"] = _noOrigShp;
stats["avg-fr"] = getAvgDist();
if (_results.size()) {
stats["max-avg-frech-dist"] = (--_results.end())->getDist();
} else {
stats["max-avg-frech-dist"] = -1;
}
stats["an-0"] =
(static_cast<double>(_an0) / static_cast<double>(_results.size())) * 100;
stats["an-5"] =
(static_cast<double>(_an5) / static_cast<double>(_results.size())) * 100;
stats["an-10"] =
(static_cast<double>(_an10) / static_cast<double>(_results.size())) * 100;
stats["an-20"] =
(static_cast<double>(_an20) / static_cast<double>(_results.size())) * 100;
stats["an-30"] =
(static_cast<double>(_an30) / static_cast<double>(_results.size())) * 100;
stats["an-50"] =
(static_cast<double>(_an50) / static_cast<double>(_results.size())) * 100;
stats["an-70"] =
(static_cast<double>(_an70) / static_cast<double>(_results.size())) * 100;
stats["an-90"] =
(static_cast<double>(_an90) / static_cast<double>(_results.size())) * 100;
return stats;
}
// _____________________________________________________________________________
std::pair<size_t, double> Collector::getDa(const std::vector<LINE>& a,
const std::vector<LINE>& b) {
assert(a.size() == b.size());
std::pair<size_t, double> ret{0, 0};
// convert (roughly) to degrees
double SEGL = 25 / util::geo::M_PER_DEG;
double MAX = 100;
for (size_t i = 0; i < a.size(); i++) {
double fdMeter = 0;
double f = util::geo::webMercDistFactor(a[i].front());
// roughly half a meter
auto aSimpl = util::geo::simplify(a[i], f * (0.5 / util::geo::M_PER_DEG));
auto bSimpl = util::geo::simplify(b[i], f * (0.5 / util::geo::M_PER_DEG));
auto old = _dACache.find(aSimpl);
if (old != _dACache.end()) {
auto match = old->second.find(bSimpl);
if (match != old->second.end()) {
fdMeter = match->second;
} else {
fdMeter = util::geo::frechetDistHav(aSimpl, bSimpl, SEGL);
_dACache[aSimpl][bSimpl] = fdMeter;
}
} else {
fdMeter = util::geo::frechetDistHav(aSimpl, bSimpl, SEGL);
_dACache[aSimpl][bSimpl] = fdMeter;
}
if (fdMeter >= MAX) {
ret.first++;
ret.second += util::geo::latLngLen(aSimpl);
}
}
return ret;
}

129
src/shapevl/Collector.h Normal file
View file

@ -0,0 +1,129 @@
// Copyright 2018, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef PFAEDLE_EVAL_COLLECTOR_H_
#define PFAEDLE_EVAL_COLLECTOR_H_
#include <fstream>
#include <map>
#include <ostream>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "pfaedle/Def.h"
#include "shapevl/Result.h"
#include "util/geo/Geo.h"
#include "util/json/Writer.h"
using ad::cppgtfs::gtfs::Shape;
using ad::cppgtfs::gtfs::Trip;
namespace pfaedle {
namespace eval {
struct lineCmp {
bool operator()(const LINE& a, const LINE& b) const {
if (a.size() != b.size()) {
return a.size() < b.size();
}
for (size_t i = 0; i < a.size(); i++) {
if (util::geo::dist(a[i], b[i]) > .00001) {
return (a[i].getX() < b[i].getX()) ||
(a[i].getX() == b[i].getX() && a[i].getY() < b[i].getY());
;
}
}
return false;
}
};
/*
* Collects routing results for evaluation
*/
class Collector {
public:
Collector(std::ostream* reportOut)
: _trips(0),
_noOrigShp(0),
_fdSum(0),
_unmatchedSegSum(0),
_unmatchedSegLengthSum(0),
_an0(0),
_an5(0),
_an10(0),
_an30(0),
_an50(0),
_an70(0),
_an90(0),
_reportOut(reportOut) {}
// Add a shape found by our tool newS for a trip t with newly calculated
// station dist values with the old shape oldS
double add(const Trip* oldT, const Shape* oldS, const Trip* newT,
const Shape* newS);
// Return the set of all Result objects
const std::set<Result>& getResults() const;
// Print general stats to os
void printStats(std::ostream* os) const;
// Print general stats to os
void printShortStats(std::ostream* os) const;
// Get JSON stats
std::map<string, double> getStats();
// Print a CSV for the results to os
void printCsv(std::ostream* os, const std::set<Result>& result) const;
// Return the averaged average frechet distance
double getAvgDist() const;
static LINE getLine(const Shape* s, std::vector<double>* dists);
double getAcc() const;
private:
std::set<Result> _results;
std::map<LINE, std::map<LINE, double, lineCmp>, lineCmp> _dCache;
std::map<LINE, std::map<LINE, double, lineCmp>, lineCmp> _dACache;
size_t _trips;
size_t _noOrigShp;
std::vector<double> _distDiffs;
std::vector<double> _hopDists;
double _fdSum;
size_t _unmatchedSegSum;
double _unmatchedSegLengthSum;
size_t _an0;
size_t _an5;
size_t _an10;
size_t _an20;
size_t _an30;
size_t _an50;
size_t _an70;
size_t _an90;
std::ostream* _reportOut;
std::pair<size_t, double> getDa(const std::vector<LINE>& a,
const std::vector<LINE>& b);
static std::vector<LINE> segmentize(
const Trip* t, const LINE& shape, const std::vector<double>& dists,
std::vector<std::pair<double, double>>& lenDist);
};
} // namespace eval
} // namespace pfaedle
#endif // PFAEDLE_EVAL_COLLECTOR_H_

View file

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

276
src/shapevl/ShapevlMain.cpp Normal file
View file

@ -0,0 +1,276 @@
// Copyright 2020, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <limits.h>
#include <stdlib.h>
#include <atomic>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include "ad/cppgtfs/Parser.h"
#include "pfaedle/router/TripTrie.h"
#include "shapevl/Collector.h"
#include "util/Misc.h"
#include "util/json/Writer.h"
#include "util/log/Log.h"
using pfaedle::router::TripTrie;
std::atomic<int> count(0);
// _____________________________________________________________________________
void printHelp(int argc, char** argv) {
UNUSED(argc);
std::cout << "Usage: " << argv[0]
<< " [-f <reportpath>] -g <gtfs> [-s] <test feeds>"
<< "\n";
std::cout
<< "\nAllowed arguments:\n -g <gtfs> Ground truth GTFS file\n";
std::cout << " -s Only output summary\n";
std::cout << " --json Output JSON\n";
std::cout << " --avg Take avg of all inputs (only for --json)\n";
std::cout << " -f <folder> Output full reports (per feed) to <folder>\n";
std::cout
<< " -m MOTs to match (GTFS MOT or string, default: all)\n";
}
// _____________________________________________________________________________
void eval(const std::vector<std::string>* paths,
std::vector<pfaedle::eval::Collector>* colls,
const std::set<Route::TYPE>* mots,
const ad::cppgtfs::gtfs::Feed* evalFeed, bool unique) {
while (1) {
int myFeed = count-- - 1;
if (myFeed < 0) return;
std::string path = (*paths)[myFeed];
LOG(DEBUG) << "Reading eval feed " << path << " ...";
ad::cppgtfs::gtfs::Feed feed;
try {
ad::cppgtfs::Parser p(path);
p.parse(&feed);
} catch (const ad::cppgtfs::ParserException& ex) {
LOG(ERROR) << "Could not parse GTFS feed " << path << ", reason was:";
std::cerr << ex.what() << std::endl;
exit(1);
}
std::vector<ad::cppgtfs::gtfs::Trip*> trips;
if (unique) {
std::map<const ad::cppgtfs::gtfs::Route*,
std::vector<TripTrie<ad::cppgtfs::gtfs::Trip>>>
forest;
for (auto t : evalFeed->getTrips()) {
auto& subForest = forest[t.second->getRoute()];
bool ins = false;
for (auto& trie : subForest) {
if (trie.addTrip(t.second,
pfaedle::router::RoutingAttrs{
t.second->getRoute()->getId(), "", ""},
false, false)) {
ins = true;
break;
}
}
if (!ins) {
subForest.resize(subForest.size() + 1);
subForest.back().addTrip(t.second,
pfaedle::router::RoutingAttrs{
t.second->getRoute()->getId(), "", ""},
false, false);
}
}
for (auto f : forest) {
for (auto sf : f.second) {
for (auto leaf : sf.getNdTrips()) {
// only one reference node
trips.push_back(leaf.second.front());
}
}
}
} else {
for (auto t : evalFeed->getTrips()) {
trips.push_back(t.second);
}
}
LOG(DEBUG) << "Evaluating " << path << "...";
size_t i = 0;
for (const auto& oldTrip : trips) {
LOG(DEBUG) << "@ " << ++i << "/" << trips.size();
if (!mots->count(oldTrip->getRoute()->getType())) continue;
auto newTrip = feed.getTrips().get(oldTrip->getId());
if (!newTrip) {
LOG(ERROR) << "Trip #" << oldTrip->getId() << " not present in " << path
<< ", skipping...";
continue;
}
(*colls)[myFeed].add(oldTrip, oldTrip->getShape(), newTrip,
newTrip->getShape());
}
}
}
// _____________________________________________________________________________
int main(int argc, char** argv) {
// disable output buffering for standard output
setbuf(stdout, NULL);
// initialize randomness
srand(time(NULL) + rand()); // NOLINT
std::string groundTruthFeedPath, motStr;
motStr = "all";
ad::cppgtfs::gtfs::Feed groundTruthFeed;
std::string fullReportPath = "";
std::vector<std::string> evlFeedPaths;
std::set<std::string> evlFeedPathsUniq;
std::vector<pfaedle::eval::Collector> evalColls;
std::vector<std::ofstream> reportStreams;
bool summarize = false;
bool json = false;
bool avg = false;
bool unique = false;
for (int i = 1; i < argc; i++) {
std::string cur = argv[i];
if (cur == "-h" || cur == "--help") {
printHelp(argc, argv);
exit(0);
} else if (cur == "-g") {
if (++i >= argc) {
LOG(ERROR) << "Missing argument for ground truth (-g).";
exit(1);
}
groundTruthFeedPath = argv[i];
} else if (cur == "-s") {
summarize = true;
} else if (cur == "--json") {
json = true;
} else if (cur == "--unique") {
unique = true;
} else if (cur == "--avg") {
avg = true;
} else if (cur == "-f") {
if (++i >= argc) {
LOG(ERROR) << "Missing argument for full reports (-f).";
exit(1);
}
fullReportPath = argv[i];
} else if (cur == "-m") {
if (++i >= argc) {
LOG(ERROR) << "Missing argument for mot (-m).";
exit(1);
}
motStr = argv[i];
} else {
char fullPath[PATH_MAX + 1];
if (!realpath(cur.c_str(), fullPath)) {
LOG(ERROR) << "Error while reading " << fullPath;
exit(1);
}
evlFeedPathsUniq.insert(fullPath);
}
}
for (const auto& feedPath : evlFeedPathsUniq) {
evlFeedPaths.push_back(feedPath);
if (fullReportPath.size()) {
reportStreams.emplace_back();
reportStreams.back().exceptions(std::ios::failbit | std::ios::badbit);
reportStreams.back().open(fullReportPath + "/" +
util::split(feedPath, '/').back() +
".fullreport.tsv");
evalColls.push_back({&reportStreams.back()});
} else {
evalColls.push_back({0});
}
count++;
}
if (groundTruthFeedPath.size() == 0) {
LOG(ERROR) << "No ground truth feed path given (-g).";
exit(1);
}
std::set<Route::TYPE> mots =
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(util::trim(motStr));
std::vector<ad::cppgtfs::gtfs::Feed> evlFeeds(evlFeedPaths.size());
try {
LOG(DEBUG) << "Reading ground truth feed" << groundTruthFeedPath << " ...";
ad::cppgtfs::Parser p(groundTruthFeedPath);
p.parse(&groundTruthFeed);
} catch (const ad::cppgtfs::ParserException& ex) {
LOG(ERROR) << "Could not parse input GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(1);
}
size_t THREADS = std::thread::hardware_concurrency();
std::vector<std::thread> thrds(THREADS);
for (auto& thr : thrds)
thr = std::thread(&eval, &evlFeedPaths, &evalColls, &mots, &groundTruthFeed,
unique);
for (auto& thr : thrds) thr.join();
if (json) {
util::json::Dict stats = {};
for (size_t i = 0; i < evalColls.size(); i++) {
util::json::Dict locStats = {};
for (const auto& kv : evalColls[i].getStats()) {
locStats[kv.first] = kv.second;
}
stats[evlFeedPaths[i]] = locStats;
}
util::json::Dict jsonStats;
if (evalColls.size() == 1) {
jsonStats = {{"statistics", stats[evlFeedPaths[0]]}};
} else {
if (avg) {
double count = evalColls.size();
std::vector<std::string> keys;
for (const auto& a : evalColls[0].getStats()) {
keys.push_back(a.first);
}
util::json::Dict avgStats;
for (const auto& k : keys) {
double sum = 0;
for (size_t i = 0; i < evalColls.size(); i++) {
sum += evalColls[i].getStats()[k];
}
avgStats[k] = sum / count;
}
jsonStats = {{"statistics", avgStats}};
} else {
jsonStats = {{"statistics", stats}};
}
}
util::json::Writer wr(&std::cout, 10, true);
wr.val(jsonStats);
wr.closeAll();
} else {
for (size_t i = 0; i < evalColls.size(); i++) {
if (summarize) {
std::cout << evlFeedPaths[i] << ": ";
evalColls[i].printShortStats(&std::cout);
std::cout << std::endl;
} else {
std::cout << " == Evaluation results for " << evlFeedPaths[i]
<< " ===" << std::endl;
evalColls[i].printStats(&std::cout);
}
}
}
}

1
src/util Submodule

@ -0,0 +1 @@
Subproject commit d1c30e9ec4cb68803be073d35beb6af2b860bda4

View file

@ -1,12 +0,0 @@
file(GLOB_RECURSE util_SRC *.cpp)
list(REMOVE_ITEM util_SRC TestMain.cpp)
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)

View file

@ -1,90 +0,0 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_MISC_H_
#define UTIL_MISC_H_
#include <cmath>
#include <chrono>
#include <sstream>
#define UNUSED(expr) do { (void)(expr); } while (0)
#define TIME() std::chrono::high_resolution_clock::now()
#define TOOK(t1, t2) (std::chrono::duration_cast<microseconds>(t2 - t1).count() / 1000.0)
#define T_START(n) auto _tstart_##n = std::chrono::high_resolution_clock::now()
#define T_STOP(n) (std::chrono::duration_cast<microseconds>(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0)
namespace util {
// cached first 10 powers of 10
static int pow10[10] = {
1, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000};
// _____________________________________________________________________________
inline uint64_t factorial(uint64_t n) {
if (n == 1) return n;
return n * factorial(n - 1);
}
// _____________________________________________________________________________
inline uint64_t atoul(const char* p) {
uint64_t ret = 0;
while (*p) {
ret = ret * 10 + (*p++ - '0');
}
return ret;
}
// _____________________________________________________________________________
inline bool isFloatingPoint(const std::string& str) {
std::stringstream ss(str);
double f;
ss >> std::noskipws >> f;
return ss.eof() && ! ss.fail();
}
// _____________________________________________________________________________
inline double atof(const char* p, uint8_t mn) {
// this atof implementation works only on "normal" float strings like
// 56.445 or -345.00, but should be faster than std::atof
double ret = 0.0;
bool neg = false;
if (*p == '-') {
neg = true;
p++;
}
while (*p >= '0' && *p <= '9') {
ret = ret * 10.0 + (*p - '0');
p++;
}
if (*p == '.') {
p++;
double f = 0;
uint8_t n = 0;
for (; n < mn && *p >= '0' && *p <= '9'; n++, p++) {
f = f * 10.0 + (*p - '0');
}
if (n < 10)
ret += f / pow10[n];
else
ret += f / std::pow(10, n);
}
if (neg) return -ret;
return ret;
}
// _____________________________________________________________________________
inline double atof(const char* p) { return atof(p, 38); }
} // namespace util
#endif // UTIL_MISC_H_

View file

@ -1,116 +0,0 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#include <stdexcept>
#ifndef UTIL_NULLABLE_H_
#define UTIL_NULLABLE_H_
namespace util {
template<typename T>
class Nullable {
public:
Nullable()
: val(), null(true) {}
Nullable(T* valPointer)
: val(), null(true) {
if (valPointer) {
assign(*valPointer);
}
}
Nullable(const T& value)
: val(value), null(false) {}
Nullable(const Nullable& other)
: val(other.val), null(other.isNull()) {}
Nullable& operator=(const Nullable& other) {
if (!other.isNull()) val = other.get();
null = other.isNull();
return *this;
}
T operator=(const T& other) {
assign(other);
return val;
}
/**
* Passing through comparision operators
*/
bool operator==(const Nullable& other) const {
return (other.isNull() && isNull()) || other.get() == get();
}
bool operator!=(const Nullable& other) const {
return !(*this == other);
}
bool operator<(const Nullable& other) const {
return !other.isNull() && !isNull() && get() < other.get();
}
bool operator>(const Nullable& other) const {
return !(*this < other || *this == other);
}
bool operator<=(const Nullable& other) const {
return *this < other || *this == other;
}
bool operator>=(const Nullable& other) const {
return *this > other || *this == other;
}
bool operator==(const T& other) const {
return !isNull() && other == get();
}
bool operator!=(const T& other) const {
return !(*this == other);
}
bool operator<(const T& other) const {
return !isNull() && get() < other;
}
bool operator>(const T& other) const {
return !(*this < other || *this == other);
}
bool operator<=(const T& other) const {
return *this < other || *this == other;
}
bool operator>=(const T& other) const {
return *this > other || *this == other;
}
operator T() const {
return get();
}
bool isNull() const {
return null;
}
T get() const {
if (!isNull()) return val;
else throw std::runtime_error("Trying to retrieve value of NULL object.");
}
private:
void assign(T v) {
val = v;
null = false;
}
T val;
bool null;
};
}
#endif // UTIL_NULLABLE_H_

View file

@ -1,231 +0,0 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_STRING_H_
#define UTIL_STRING_H_
#include <algorithm>
#include <cstring>
#include <sstream>
#include <string>
#include <vector>
namespace util {
// _____________________________________________________________________________
inline std::string urlDecode(const std::string& encoded) {
std::string decoded;
for (size_t i = 0; i < encoded.size(); ++i) {
char c = encoded[i];
if (c == '%') {
std::string ah = encoded.substr(i + 1, 2);
char* nonProced = 0;
char hexVal = strtol(ah.c_str(), &nonProced, 16);
if (ah.find_first_of("+-") > 1 && ah.size() - strlen(nonProced) == 2) {
c = hexVal;
i += 2;
}
} else if (c == '+') {
c = ' ';
}
decoded += c;
}
return decoded;
}
// _____________________________________________________________________________
inline std::string jsonStringEscape(const std::string& unescaped) {
std::string escaped;
for (size_t i = 0; i < unescaped.size(); ++i) {
if (unescaped[i] == '"' || unescaped[i] == '\\') {
escaped += "\\";
}
if (iscntrl(unescaped[i])) {
escaped += " ";
}
escaped += unescaped[i];
}
return escaped;
}
// _____________________________________________________________________________
inline bool replace(std::string& subj, const std::string& from,
const std::string& to) {
if (from.empty()) return false;
size_t start_pos = subj.find(from);
if (start_pos != std::string::npos) {
subj.replace(start_pos, from.length(), to);
return true;
}
return false;
}
// _____________________________________________________________________________
inline bool replaceAll(std::string& subj, const std::string& from,
const std::string& to) {
if (from.empty()) return false;
bool found = false;
size_t s = subj.find(from, 0);
for (; s != std::string::npos; s = subj.find(from, s + to.length())) {
found = true;
subj.replace(s, from.length(), to);
}
return found;
}
// _____________________________________________________________________________
inline std::string unixBasename(const std::string& pathname) {
return {std::find_if(pathname.rbegin(), pathname.rend(),
[](char c) { return c == '/'; })
.base(),
pathname.end()};
}
// _____________________________________________________________________________
template <typename T>
inline std::string toString(T obj) {
std::stringstream ss;
ss << obj;
return ss.str();
}
// _____________________________________________________________________________
inline std::vector<std::string> split(std::string in, char sep) {
std::stringstream ss(in);
std::vector<std::string> ret(1);
while (std::getline(ss, ret.back(), sep)) {
ret.push_back("");
}
ret.pop_back();
return ret;
}
// _____________________________________________________________________________
inline std::string ltrim(std::string str) {
str.erase(0, str.find_first_not_of(" \t\n\v\f\r"));
return str;
}
// _____________________________________________________________________________
inline std::string rtrim(std::string str) {
str.erase(str.find_last_not_of(" \t\n\v\f\r") + 1);
return str;
}
// _____________________________________________________________________________
inline std::string trim(std::string str) { return ltrim(rtrim(str)); }
// _____________________________________________________________________________
inline size_t editDist(const std::string& s1, const std::string& s2) {
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
size_t len1 = s1.size();
size_t len2 = s2.size();
std::vector<size_t> cur(len2 + 1);
std::vector<size_t> prev(len2 + 1);
for (size_t i = 0; i < prev.size(); i++) prev[i] = i;
for (size_t i = 0; i < len1; i++) {
cur[0] = i + 1;
for (size_t j = 0; j < len2; j++) {
cur[j + 1] =
std::min(prev[1 + j] + 1,
std::min(cur[j] + 1, prev[j] + (s1[i] == s2[j] ? 0 : 1)));
}
std::swap(cur, prev);
}
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());
}
// _____________________________________________________________________________
inline std::string toUpper(std::string str) {
std::transform(str.begin(), str.end(),str.begin(), toupper);
return str;
}
// _____________________________________________________________________________
inline std::string toLower(std::string str) {
std::transform(str.begin(), str.end(),str.begin(), tolower);
return str;
}
// _____________________________________________________________________________
template <class Iter>
inline std::string implode(Iter begin, const Iter& end, const char* del) {
std::stringstream ss;
size_t i = 0;
while (begin != end) {
if (i != 0) ss << del;
ss << *begin;
begin++;
i++;
}
return ss.str();
}
// _____________________________________________________________________________
inline std::string normalizeWhiteSpace(const std::string& input) {
std::string ret;
bool ws = false;
for (size_t i = 0; i < input.size(); i++) {
if (std::isspace(input[i])) {
if (!ws) {
ret += " ";
ws = true;
}
continue;
} else {
ws = false;
ret += input[i];
}
}
return ret;
}
// _____________________________________________________________________________
template <typename T>
inline std::string implode(const std::vector<T>& vec, const char* del) {
return implode(vec.begin(), vec.end(), del);
}
}
#endif // UTIL_STRING_H_

View file

@ -1,54 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_BEZIERCURVE_H_
#define UTIL_GEO_BEZIERCURVE_H_
#include <vector>
#include "util/geo/Geo.h"
#include "util/geo/PolyLine.h"
namespace util {
namespace geo {
struct CubicPolynom {
CubicPolynom(double a, double b, double c, double d, double x)
: a(a), b(b), c(c), d(d), x(x) {}
CubicPolynom() : a(0), b(0), c(0), d(0), x(0) {}
double a, b, c, d, x;
double valueAt(double x) const;
};
/**
* Bezier curve
*/
template <typename T>
class BezierCurve {
public:
BezierCurve(const Point<T>& a, const Point<T>& b, const Point<T>& c, const Point<T>& d);
const PolyLine<T>& render(double d);
private:
double _d;
// the x and y polynoms for this spline
CubicPolynom _xp, _yp;
// store the rendered polyline for quicker access
PolyLine<T> _rendered;
bool _didRender;
void recalcPolynoms(const Point<T>& x, const Point<T>& b, const Point<T>& c,
const Point<T>& d);
Point<T> valueAt(double t) const;
};
#include "util/geo/BezierCurve.tpp"
}
}
#endif // UTIL_GEO_BEZIERCURVE_H_

View file

@ -1,70 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
// _____________________________________________________________________________
template <typename T>
BezierCurve<T>::BezierCurve(const Point<T>& a, const Point<T>& b,
const Point<T>& c, const Point<T>& d)
: _d(dist(a, d)) {
assert(_d > 0);
recalcPolynoms(a, b, c, d);
}
// _____________________________________________________________________________
template <typename T>
void BezierCurve<T>::recalcPolynoms(const Point<T>& a, const Point<T>& b,
const Point<T>& c, const Point<T>& d) {
_xp.a = a.getX();
_xp.b = 3.0 * (b.getX() - a.getX());
_xp.c = 3.0 * (c.getX() - b.getX()) - _xp.b;
_xp.d = d.getX() - a.getX() - _xp.c - _xp.b;
_yp.a = a.getY();
_yp.b = 3.0 * (b.getY() - a.getY());
_yp.c = 3.0 * (c.getY() - b.getY()) - _yp.b;
_yp.d = d.getY() - a.getY() - _yp.c - _yp.b;
_didRender = false;
}
// _____________________________________________________________________________
template <typename T>
Point<T> BezierCurve<T>::valueAt(double t) const {
return Point<T>(_xp.valueAt(t), _yp.valueAt(t));
}
// _____________________________________________________________________________
template <typename T>
const PolyLine<T>& BezierCurve<T>::render(double d) {
assert(d > 0);
if (_didRender) return _rendered;
if (_d == 0) {
_rendered << Point<T>(_xp.a, _yp.a) << Point<T>(_xp.a, _yp.a);
return _rendered;
}
_rendered.empty();
double n = _d / d, dt = 1 / n, t = 0;
bool cancel = false;
while (true) {
_rendered << valueAt(t);
t += dt;
if (cancel) break;
if (t > 1) {
t = 1;
cancel = true;
}
}
_didRender = true;
return _rendered;
}
// _____________________________________________________________________________
double CubicPolynom::valueAt(double atx) const {
double dx = atx - x;
return a + b * dx + c * dx * dx + d * dx * dx * dx;
}

View file

@ -1,91 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_BOX_H_
#define UTIL_GEO_BOX_H_
#include "./Point.h"
namespace util {
namespace geo {
template <typename T>
class Box {
public:
// maximum inverse box as default value of box
Box()
: _ll(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()),
_ur(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()) {}
Box(const Point<T>& ll, const Point<T>& ur) : _ll(ll), _ur(ur) {}
const Point<T>& getLowerLeft() const { return _ll; }
const Point<T>& getUpperRight() const { return _ur; }
Point<T>& getLowerLeft() { return _ll; }
Point<T>& getUpperRight() { return _ur; }
void setLowerLeft(const Point<T>& ll) { _ll = ll; }
void setUpperRight(const Point<T>& ur) { _ur = ur; }
bool operator==(const Box<T>& b) const {
return getLowerLeft() == b.getLowerLeft() &&
getUpperRight() == b.getUpperRight();
}
bool operator!=(const Box<T>& p) const { return !(*this == p); }
private:
Point<T> _ll, _ur;
};
template <typename T>
class RotatedBox {
public:
RotatedBox() : _box(), _deg(0), _center() {}
RotatedBox(const Box<T>& box)
: _box(box),
_deg(0),
_center(Point<T>(
(box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2),
(box.getUpperRight().getY() - box.getLowerLeft().getY()) / T(2))) {}
RotatedBox(const Point<T>& ll, const Point<T>& ur)
: _box(ll, ur),
_deg(0),
_center(Point<T>((ur.getX() - ll.getX()) / T(2),
(ur.getY() - ll.getY()) / T(2))) {}
RotatedBox(const Box<T>& box, double deg)
: _box(box),
_deg(deg),
_center(Point<T>(
(box.getUpperRight().getX() - box.getLowerLeft().getX()) / T(2),
(box.getUpperRight().getY() - box.getLowerLeft().getY()) / T(2))) {}
RotatedBox(const Point<T>& ll, const Point<T>& ur, double deg)
: _box(ll, ur),
_deg(deg),
_center(Point<T>((ur.getX() - ll.getX()) / T(2),
(ur.getY() - ll.getY()) / T(2))) {}
RotatedBox(const Box<T>& box, double deg, const Point<T>& center)
: _box(box), _deg(deg), _center(center) {}
RotatedBox(const Point<T>& ll, const Point<T>& ur, double deg,
const Point<T>& center)
: _box(ll, ur), _deg(deg), _center(center) {}
const Box<T>& getBox() const { return _box; }
Box<T>& getBox() { return _box; }
double getDegree() const { return _deg; }
const Point<T>& getCenter() const { return _center; }
Point<T>& getCenter() { return _center; }
void setDegree(double deg) { _deg = deg; }
private:
Box<T> _box;
double _deg;
Point<T> _center;
};
} // namespace geo
} // namespace util
#endif // UTIL_GEO_BOX_H_

File diff suppressed because it is too large Load diff

View file

@ -1,32 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEOGRAPH_H_
#define UTIL_GEOGRAPH_H_
#include <map>
#include "util/geo/Geo.h"
#include "util/json/Writer.h"
namespace util {
namespace geograph {
template<typename T>
class GeoEdgePL {
public:
virtual const util::geo::Line<T>* getGeom() const = 0;
virtual json::Dict getAttrs() const = 0;
};
template<typename T>
class GeoNodePL {
public:
virtual const util::geo::Point<T>* getGeom() const = 0;
virtual json::Dict getAttrs() const = 0;
};
} // namespace geograph
} // namespace util
#endif // UTIL_GEOGRAPH_H_

View file

@ -1,89 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_GRID_H_
#define UTIL_GEO_GRID_H_
#include <set>
#include <vector>
#include <map>
#include "util/geo/Geo.h"
namespace util {
namespace geo {
class GridException : public std::runtime_error {
public:
GridException(std::string const& msg) : std::runtime_error(msg) {}
};
template <typename V, template <typename> class G, typename T>
class Grid {
public:
// initialization of a point grid with cell width w and cell height h
// that covers the area of bounding box bbox
Grid(double w, double h, const Box<T>& bbox);
// initialization of a point grid with cell width w and cell height h
// that covers the area of bounding box bbox
// optional parameters specifies whether a value->cell index
// should be kept (true by default!)
Grid(double w, double h, const Box<T>& bbox, bool buildValIdx);
// the empty grid
Grid();
// the empty grid
Grid(bool buildValIdx);
// add object t to this grid
void add(G<T> geom, V val);
void add(size_t x, size_t y, V val);
void get(const Box<T>& btbox, std::set<V>* s) const;
void get(const G<T>& geom, double d, std::set<V>* s) const;
void get(size_t x, size_t y, std::set<V>* s) const;
void remove(V val);
void getNeighbors(const V& val, double d, std::set<V>* s) const;
void getCellNeighbors(const V& val, size_t d, std::set<V>* s) const;
void getCellNeighbors(size_t x, size_t y, size_t xPerm, size_t yPerm,
std::set<V>* s) const;
std::set<std::pair<size_t, size_t> > getCells(const V& val) const;
size_t getXWidth() const;
size_t getYHeight() const;
private:
double _width;
double _height;
double _cellWidth;
double _cellHeight;
Box<T> _bb;
size_t _counter;
size_t _xWidth;
size_t _yHeight;
bool _hasValIdx;
std::vector<std::vector<std::set<V> > > _grid;
std::map<V, std::set<std::pair<size_t, size_t> > > _index;
std::set<V> _removed;
Box<T> getBox(size_t x, size_t y) const;
size_t getCellXFromX(double lon) const;
size_t getCellYFromY(double lat) const;
};
#include "util/geo/Grid.tpp"
} // namespace geo
} // namespace util
#endif // UTIL_GEO_GRID_H_

View file

@ -1,228 +0,0 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author: Patrick Brosi <brosip@informatik.uni-freiburg.de>
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
Grid<V, G, T>::Grid(bool bldIdx)
: _width(0),
_height(0),
_cellWidth(0),
_cellHeight(0),
_xWidth(0),
_yHeight(0),
_hasValIdx(bldIdx) {}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
Grid<V, G, T>::Grid() : Grid<V, G, T>(true) {}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
Grid<V, G, T>::Grid(double w, double h, const Box<T>& bbox)
: Grid<V, G, T>(w, h, bbox, true) {}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
Grid<V, G, T>::Grid(double w, double h, const Box<T>& bbox, bool bValIdx)
: _cellWidth(fabs(w)),
_cellHeight(fabs(h)),
_bb(bbox),
_hasValIdx(bValIdx) {
_width =
bbox.getUpperRight().getX() - bbox.getLowerLeft().getX();
_height =
bbox.getUpperRight().getY() - bbox.getLowerLeft().getY();
if (_width < 0 || _height < 0) {
_width = 0;
_height = 0;
_xWidth = 0;
_yHeight = 0;
return;
}
_xWidth = ceil(_width / _cellWidth);
_yHeight = ceil(_height / _cellHeight);
// resize rows
_grid.resize(_xWidth);
// resize columns
for (size_t i = 0; i < _xWidth; i++) {
_grid[i].resize(_yHeight);
}
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::add(G<T> geom, V val) {
Box<T> box = getBoundingBox(geom);
size_t swX = getCellXFromX(box.getLowerLeft().getX());
size_t swY = getCellYFromY(box.getLowerLeft().getY());
size_t neX = getCellXFromX(box.getUpperRight().getX());
size_t neY = getCellYFromY(box.getUpperRight().getY());
for (size_t x = swX; x <= neX && x < _grid.size(); x++) {
for (size_t y = swY; y <= neY && y < _grid[x].size(); y++) {
if (intersects(geom, getBox(x, y))) {
add(x, y, val);
}
}
}
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::add(size_t x, size_t y, V val) {
_grid[x][y].insert(val);
if (_hasValIdx) _index[val].insert(std::pair<size_t, size_t>(x, y));
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::get(const Box<T>& box, std::set<V>* s) const {
size_t swX = getCellXFromX(box.getLowerLeft().getX());
size_t swY = getCellYFromY(box.getLowerLeft().getY());
size_t neX = getCellXFromX(box.getUpperRight().getX());
size_t neY = getCellYFromY(box.getUpperRight().getY());
for (size_t x = swX; x <= neX && x >= 0 && x < _xWidth; x++)
for (size_t y = swY; y <= neY && y >= 0 && y < _yHeight; y++) get(x, y, s);
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::get(const G<T>& geom, double d, std::set<V>* s) const {
Box<T> a = getBoundingBox(geom);
Box<T> b(Point<T>(a.getLowerLeft().getX() - d,
a.getLowerLeft().getY() - d),
Point<T>(a.getUpperRight().getX() + d,
a.getUpperRight().getY() + d));
return get(b, s);
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::get(size_t x, size_t y, std::set<V>* s) const {
if (_hasValIdx) {
s->insert(_grid[x][y].begin(), _grid[x][y].end());
} else {
// if we dont have a value index, we have a set of deleted nodes.
// in this case, only insert if not deleted
std::copy_if(_grid[x][y].begin(), _grid[x][y].end(),
std::inserter(*s, s->end()),
[&](const V& v) { return Grid<V, G, T>::_removed.count(v) == 0; });
}
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::remove(V val) {
if (_hasValIdx) {
auto i = _index.find(val);
if (i == _index.end()) return;
for (auto pair : i->second) {
_grid[pair.first][pair.second].erase(
_grid[pair.first][pair.second].find(val));
}
_index.erase(i);
} else {
_removed.insert(val);
}
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::getNeighbors(const V& val, double d, std::set<V>* s) const {
if (!_hasValIdx) throw GridException("No value index build!");
auto it = _index.find(val);
if (it == _index.end()) return;
size_t xPerm = ceil(d / _cellWidth);
size_t yPerm = ceil(d / _cellHeight);
for (auto pair : it->second) {
getCellNeighbors(pair.first, pair.second, xPerm, yPerm, s);
}
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::getCellNeighbors(const V& val, size_t d,
std::set<V>* s) const {
if (!_hasValIdx) throw GridException("No value index build!");
auto it = _index.find(val);
if (it == _index.end()) return;
for (auto pair : it->second) {
getCellNeighbors(pair.first, pair.second, d, d, s);
}
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
void Grid<V, G, T>::getCellNeighbors(size_t cx, size_t cy, size_t xPerm,
size_t yPerm, std::set<V>* s) const {
size_t swX = xPerm > cx ? 0 : cx - xPerm;
size_t swY = yPerm > cy ? 0 : cy - yPerm;
size_t neX = xPerm + cx + 1 > _xWidth ? _xWidth : cx + xPerm + 1;
size_t neY = yPerm + cy + 1 > _yHeight ? _yHeight : cy + yPerm + 1;
for (size_t x = swX; x < neX; x++) {
for (size_t y = swY; y < neY; y++) {
s->insert(_grid[x][y].begin(), _grid[x][y].end());
}
}
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
std::set<std::pair<size_t, size_t> > Grid<V, G, T>::getCells(
const V& val) const {
if (!_hasValIdx) throw GridException("No value index build!");
return _index.find(val)->second;
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
Box<T> Grid<V, G, T>::getBox(size_t x, size_t y) const {
Point<T> sw(_bb.getLowerLeft().getX() + x * _cellWidth,
_bb.getLowerLeft().getY() + y * _cellHeight);
Point<T> ne(_bb.getLowerLeft().getX() + (x + 1) * _cellWidth,
_bb.getLowerLeft().getY() + (y + 1) * _cellHeight);
return Box<T>(sw, ne);
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
size_t Grid<V, G, T>::getCellXFromX(double x) const {
float dist = x - _bb.getLowerLeft().getX();
if (dist < 0) dist = 0;
return floor(dist / _cellWidth);
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
size_t Grid<V, G, T>::getCellYFromY(double y) const {
float dist = y - _bb.getLowerLeft().getY();
if (dist < 0) dist = 0;
return floor(dist / _cellHeight);
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
size_t Grid<V, G, T>::getXWidth() const {
return _xWidth;
}
// _____________________________________________________________________________
template <typename V, template <typename> class G, typename T>
size_t Grid<V, G, T>::getYHeight() const {
return _yHeight;
}

View file

@ -1,28 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_LINE_H_
#define UTIL_GEO_LINE_H_
#include <vector>
#include "./Point.h"
namespace util {
namespace geo {
template <typename T>
class Line : public std::vector<Point<T>> {
using std::vector<Point<T>>::vector;
};
template <typename T>
using LineSegment = std::pair<Point<T>, Point<T>>;
template <typename T>
using MultiLine = std::vector<Line<T>>;
} // namespace geo
} // namespace util
#endif // UTIL_GEO_LINE_H_

View file

@ -1,50 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_POINT_H_
#define UTIL_GEO_POINT_H_
#include <vector>
namespace util {
namespace geo {
template <typename T>
class Point {
public:
Point() : _x(0), _y(0) {}
Point(T x, T y) : _x(x), _y(y) {}
T getX() const { return _x; }
T getY() const { return _y; }
void setX(T x) { _x = x; }
void setY(T y) { _y = y; }
Point<T> operator+(const Point<T>& p) const {
return Point<T>(_x + p.getX(), _y + p.getY());
}
Point<T> operator-(const Point<T>& p) const {
return Point<T>(_x - p.getX(), _y - p.getY());
}
bool operator==(const Point<T>& p) const {
return p.getX() == _x && p.getY() == _y;
}
bool operator!=(const Point<T>& p) const {
return !(*this == p);
}
private:
T _x, _y;
};
template <typename T>
using MultiPoint = std::vector<Point<T>>;
} // namespace geo
} // namespace util
#endif // UTIL_GEO_POINT_H_

View file

@ -1,143 +0,0 @@
// Copyright 2016, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_GEO_POLYLINE_H_
#define UTIL_GEO_POLYLINE_H_
#include <cfloat>
#include <ostream>
#include <iomanip>
#include <string>
#include <set>
#include <vector>
#include "Geo.h"
namespace util {
namespace geo {
static const double MAX_EQ_DISTANCE = 15;
static const double AVERAGING_STEP = 20;
// legacy code, will be removed in the future
template <typename T>
struct LinePoint {
LinePoint() : lastIndex(0), totalPos(-1), p() {}
LinePoint(size_t i, double pos, const Point<T>& p)
: lastIndex(i), totalPos(pos), p(p) {}
size_t lastIndex;
double totalPos;
Point<T> p;
};
template <typename T>
struct LinePointCmp {
bool operator()(const LinePoint<T>& lh, const LinePoint<T>& rh) const {
return lh.totalPos < rh.totalPos;
}
};
template <typename T>
using LinePointPair = std::pair<LinePoint<T>, LinePoint<T>>;
template <typename T>
using SharedSegment = std::pair<LinePointPair<T>, LinePointPair<T>>;
template <typename T>
struct SharedSegments {
std::vector<SharedSegment<T>> segments;
};
template <typename T>
class PolyLine {
public:
PolyLine();
PolyLine(const Point<T>& from, const Point<T>& to);
PolyLine(const Line<T>& l);
PolyLine& operator<<(const Point<T>& p);
PolyLine& operator>>(const Point<T>& p);
void reverse();
PolyLine getReversed() const;
void offsetPerp(double units);
PolyLine getPerpOffsetted(double units) const;
const Line<T>& getLine() const;
double distTo(const PolyLine<T>& g) const;
double distTo(const Point<T>& p) const;
SharedSegments<T> getSharedSegments(const PolyLine<T>& pl, double dmax) const;
double getLength() const;
// return point at dist
LinePoint<T> getPointAtDist(double dist) const;
// return point at [0..1]
LinePoint<T> getPointAt(double dist) const;
PolyLine<T> getSegment(double a, double b) const;
PolyLine<T> getSegmentAtDist(double dista, double distb) const;
PolyLine<T> getSegment(const LinePoint<T>& start, const LinePoint<T>& end) const;
PolyLine<T> getSegment(const Point<T>& a, const Point<T>& b) const;
std::set<LinePoint<T>, LinePointCmp<T>> getIntersections(const PolyLine<T>& g) const;
static PolyLine<T> average(const std::vector<const PolyLine<T>*>& lines);
static PolyLine<T> average(const std::vector<const PolyLine<T>*>& lines,
const std::vector<double>& weights);
void simplify(double d);
void empty();
void smoothenOutliers(double d);
std::pair<size_t, double> nearestSegment(const Point<T>& p) const;
std::pair<size_t, double> nearestSegmentAfter(const Point<T>& p,
size_t after) const;
LinePoint<T> projectOn(const Point<T>& p) const;
LinePoint<T> projectOnAfter(const Point<T>& p, size_t after) const;
void move(double vx, double vy);
std::pair<double, double> getSlopeBetween(double ad, double bd) const;
std::pair<double, double> getSlopeBetweenDists(double ad, double bd) const;
// equality operator, will hold frechet-distance equality check in
// the dmax
bool operator==(const PolyLine& rhs) const;
bool contains(const PolyLine& rhs, double dmax) const;
bool equals(const PolyLine& rhs) const;
bool equals(const PolyLine& rhs, double dmax) const;
std::string getWKT() const;
PolyLine getOrthoLineAtDist(double d, double lengt) const;
Point<T> interpolate(const Point<T>& a, const Point<T>& b, double p) const;
void fixTopology(double maxl);
void applyChaikinSmooth(size_t depth);
const Point<T>& front() const;
const Point<T>& back() const;
private:
std::set<LinePoint<T>, LinePointCmp<T>> getIntersections(const PolyLine& p,
size_t a, size_t b) const;
Line<T> _line;
};
#include "util/geo/PolyLine.tpp"
} // namespace geo
} // namespace util
#endif // UTIL_GEO_POLYLINE_H_

Some files were not shown because too many files have changed in this diff Show more