Compare commits
258 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2f9679c22 | ||
|
|
172f7eafdb | ||
|
|
eb2a556276 | ||
|
|
0a2f4eecda | ||
|
|
e779970387 | ||
|
|
ea8185a784 | ||
|
|
33508404e1 | ||
|
|
e503fba5b6 | ||
|
|
6a1ae5fc77 | ||
|
|
e643166555 | ||
|
|
7ccc303022 | ||
|
|
0037a83f68 | ||
|
|
67b9f7f6d2 | ||
|
|
eda6f50fbb | ||
|
|
5ced5e5a50 | ||
|
|
76c1324353 | ||
|
|
1752da6147 | ||
|
|
a6b09de921 | ||
|
|
9a5fc2abdf | ||
|
|
ad0252695f | ||
|
|
a078b22e18 | ||
|
|
81a8d9d65b | ||
|
|
d87eacb6e0 | ||
|
|
22bc04aa9d | ||
|
|
136fae1cf1 | ||
|
|
3b35ef638d | ||
|
|
80504b21fe | ||
|
|
6bbb461b02 | ||
|
|
83851eed49 | ||
|
|
6adfc5feff | ||
|
|
de9583877c | ||
|
|
be3c41b784 | ||
|
|
9d00fa2d75 | ||
|
|
62e2ea36e1 | ||
|
|
dd05506c42 | ||
|
|
dfb62babd9 | ||
|
|
096d6800df | ||
|
|
363c52252d | ||
|
|
19350108ce | ||
|
|
3e350185f8 | ||
|
|
500a257ca3 | ||
|
|
aa674f81ae | ||
|
|
f6228ee7a2 | ||
|
|
cb606ef75f | ||
|
|
7a772486d0 | ||
|
|
1c459816de | ||
|
|
c27f8a852f | ||
|
|
1b7ade99be | ||
|
|
1d8ce1aa7c | ||
|
|
3f43538010 | ||
|
|
580f1807f3 | ||
|
|
a8c23b2c02 | ||
|
|
56ab60c3fd | ||
|
|
194f37fcd5 | ||
|
|
57ee83644e | ||
|
|
5c1ad4579f | ||
|
|
646707628f | ||
|
|
4f14dad215 | ||
|
|
58c47bb1a5 | ||
|
|
3d6ed3e2fd | ||
|
|
d43d9fc908 | ||
|
|
ed15d5d1de | ||
|
|
25f6fb38b2 | ||
|
|
33a92077df | ||
|
|
45f508bf34 | ||
|
|
c0c2da57e7 | ||
|
|
43904f2346 | ||
|
|
16a8f3fcad | ||
|
|
bb21a9651d | ||
|
|
e085cfb1b2 | ||
|
|
59d4f96c68 | ||
|
|
3e6e7ebfed | ||
|
|
167753142c | ||
|
|
496ee739bc | ||
|
|
29f5b9bd10 | ||
|
|
6f6262f9b1 | ||
|
|
79dd70c2f5 | ||
|
|
f71207159c | ||
|
|
57545ced30 | ||
|
|
0699fccb1e | ||
|
|
c68cde67dc | ||
|
|
9a8e5d4e31 | ||
|
|
b4b08baeca | ||
|
|
0209c6fa04 | ||
|
|
760ee20c35 | ||
|
|
83a073ab0b | ||
|
|
d059870f8c | ||
|
|
d60c291402 | ||
|
|
c5ebfbb57b | ||
|
|
e8b3b33d3c | ||
|
|
8fb63b80bd | ||
|
|
04de10a3ae | ||
|
|
8f6be38f8c | ||
|
|
6f7b4a5694 | ||
|
|
f867e2bb08 | ||
|
|
361063185e | ||
|
|
a58ed3d54d | ||
|
|
e15b90af9f | ||
|
|
13b6436a41 | ||
|
|
af4d2293a6 | ||
|
|
db437e1d95 | ||
|
|
760998144f | ||
|
|
647a596dea | ||
|
|
cdbfb0966d | ||
|
|
e402f19cdb | ||
|
|
396ea175e7 | ||
|
|
54409ec24d | ||
|
|
ff8748ef3f | ||
|
|
15e84d930a | ||
|
|
c3bc12eb8c | ||
|
|
46482788ff | ||
|
|
39fb167cbf | ||
|
|
ea79e3ac52 | ||
|
|
516f04b9c5 | ||
|
|
749044ce97 | ||
|
|
1e516f4d2b | ||
|
|
1febc192ce | ||
|
|
c25d174e60 | ||
|
|
6473dcdb52 | ||
|
|
f10397db41 | ||
|
|
0e5213f049 | ||
|
|
6b445b84d1 | ||
|
|
3a08b63d8e | ||
|
|
4c29892658 | ||
|
|
f1822868c5 | ||
|
|
57de18844f | ||
|
|
59e527bcb4 | ||
|
|
364d6a19e5 | ||
|
|
cfc41d01e7 | ||
|
|
16e66e59a5 | ||
|
|
366e4b2aba | ||
|
|
deaaf23caf | ||
|
|
cea4474631 | ||
|
|
e0e4ee1f2b | ||
|
|
087ea0310b | ||
|
|
73101fcc03 | ||
|
|
dd64e075c8 | ||
|
|
8fe7a30e31 | ||
|
|
2f7e6a028f | ||
|
|
5d2bb9bbfd | ||
|
|
b9897bdd97 | ||
|
|
9a8a64d38e | ||
|
|
4aa500b867 | ||
|
|
2a01eda564 | ||
|
|
b0a2cff43a | ||
|
|
08b0685ad1 | ||
|
|
e06c15656b | ||
|
|
c023bb963d | ||
|
|
69dc466605 | ||
|
|
9c51f2799d | ||
|
|
6c42f8244e | ||
|
|
1163f11e57 | ||
|
|
3eb5c881fe | ||
|
|
6d2671900b | ||
|
|
f856e9865c | ||
|
|
9262b3ad45 | ||
|
|
31f9cb72ff | ||
|
|
a5931eb9ec | ||
|
|
06aefa538a | ||
|
|
feacae92ef | ||
|
|
27da2a9c9e | ||
|
|
bfca604ab5 | ||
|
|
79303f12f2 | ||
|
|
df1ba83a53 | ||
|
|
347badf770 | ||
|
|
9d19e344d3 | ||
|
|
67308d02e9 | ||
|
|
ee948a8810 | ||
|
|
0db43ad457 | ||
|
|
78f9eb7044 | ||
|
|
4cf1cc2b39 | ||
|
|
9d1123e86e | ||
|
|
a2e5b3be32 | ||
|
|
f6f1de05d2 | ||
|
|
1dff5eaf64 | ||
|
|
d995f77b53 | ||
|
|
670bae2585 | ||
|
|
3e8672a268 | ||
|
|
ff7f810591 | ||
|
|
e00e536bf2 | ||
|
|
c978217114 | ||
|
|
02bda75f01 | ||
|
|
549cc90616 | ||
|
|
89be7a1760 | ||
|
|
4733b0c676 | ||
|
|
2fb157ef37 | ||
|
|
8fb21919f3 | ||
|
|
7835242cb8 | ||
|
|
de787243c1 | ||
|
|
72bc6106ae | ||
|
|
73ad8fdb96 | ||
|
|
50bbbc044d | ||
|
|
eff4ee4521 | ||
|
|
118beffe71 | ||
|
|
63f0b61ea1 | ||
|
|
2cc2d2dc23 | ||
|
|
9816b93264 | ||
|
|
cc8067d90f | ||
|
|
2aef6cac5d | ||
|
|
e10b090999 | ||
|
|
69a360c48f | ||
|
|
95ce5ba03d | ||
|
|
dbceb0ef74 | ||
|
|
8446db5c4b | ||
|
|
486a8136fd | ||
|
|
967963e180 | ||
|
|
18c00718f6 | ||
|
|
dfce1a44fc | ||
|
|
10b72785ba | ||
|
|
03aa3c5a7d | ||
|
|
713262f756 | ||
|
|
f56294b597 | ||
|
|
64b83f569c | ||
|
|
04e87c1d9c | ||
|
|
5703eb47bd | ||
|
|
7f0443243c | ||
|
|
45027d5496 | ||
|
|
4220ad6efb | ||
|
|
e584eb7cbd | ||
|
|
ad04f554a5 | ||
|
|
7390efab8d | ||
|
|
2f356239bc | ||
|
|
eb415cd234 | ||
|
|
df2ab2df77 | ||
|
|
1a9482a445 | ||
|
|
91728a6f58 | ||
|
|
c0d89c3bf0 | ||
|
|
f516ba0354 | ||
|
|
30883e6487 | ||
|
|
0effea29bd | ||
|
|
105d639380 | ||
|
|
31a0c50892 | ||
|
|
9712e06a02 | ||
|
|
13fb21f626 | ||
|
|
63dfda9749 | ||
|
|
4710b4ae20 | ||
|
|
3af004e49b | ||
|
|
223ffa6f6b | ||
|
|
d387314715 | ||
|
|
4674f0f116 | ||
|
|
3e362457fc | ||
|
|
d342062855 | ||
|
|
b07110840e | ||
|
|
cf79e67631 | ||
|
|
73eb2c16da | ||
|
|
80b4922b02 | ||
|
|
73d159f449 | ||
|
|
4fb31679d9 | ||
|
|
a0c7d3a238 | ||
|
|
7ff625ffe2 | ||
|
|
871bdc97c4 | ||
|
|
a47ccde984 | ||
|
|
a6886d99d7 | ||
|
|
314a22c575 | ||
|
|
dacf7b29df | ||
|
|
95e60a0833 | ||
|
|
ea32f7c250 | ||
|
|
ea0e9e73fc |
139 changed files with 11121 additions and 20309 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/build
|
||||
|
||||
/Dockerfile
|
||||
gtfs-out
|
||||
33
.forgejo/workflows/test.yml
Normal file
33
.forgejo/workflows/test.yml
Normal 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
7
.gitmodules
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,30 +1,24 @@
|
|||
cmake_minimum_required (VERSION 2.8)
|
||||
cmake_minimum_required (VERSION 3.5)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
project (pfaedle)
|
||||
|
||||
set(CPPLINT "${CMAKE_SOURCE_DIR}/cpplint.py")
|
||||
include(cmake/cpplint.cmake)
|
||||
|
||||
set(CPPLINT_PROJECT_ROOT "src")
|
||||
if (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()
|
||||
|
||||
enable_testing()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
|
||||
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build")
|
||||
|
||||
|
||||
find_package(Boost COMPONENTS system filesystem REQUIRED)
|
||||
include_directories("build" ${Boost_INCLUDE_DIRS})
|
||||
|
||||
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
|
||||
set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -frename-registers -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -I/usr/lib/gcc/x86_64-linux-gnu/4.8/include/ -I/usr/lib/gcc/x86_64-linux-gnu/5/include/")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DLOGLEVEL=3")
|
||||
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")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -g -DLOGLEVEL=3")
|
||||
|
|
@ -32,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.7 OR GCC_VERSION VERSION_EQUAL 4.7))
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif (GCC_VERSION VERSION_EQUAL 4.6)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
else ()
|
||||
message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.6 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")
|
||||
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()
|
||||
|
||||
if (ZLIB_FOUND)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZLIB_FOUND=1")
|
||||
else ()
|
||||
message(FATAL_ERROR "Your C++ compiler does not support C++11.")
|
||||
endif ()
|
||||
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)
|
||||
|
|
@ -60,27 +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("Utility Tests" utilTest)
|
||||
|
||||
# custom eval target
|
||||
|
||||
add_custom_target(
|
||||
eval
|
||||
COMMAND make
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval
|
||||
)
|
||||
add_test("utilTest" utilTest)
|
||||
add_test("pfaedleTest" pfaedleTest)
|
||||
|
||||
# handles install target
|
||||
|
||||
install(
|
||||
FILES README.md pfaedle.cfg DESTINATION share/${PROJECT_NAME} 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
|
||||
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
25
Dockerfile
Normal 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"]
|
||||
674
LICENSE
Normal file
674
LICENSE
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
123
README.md
123
README.md
|
|
@ -1,65 +1,71 @@
|
|||
```
|
||||
_-====-__-=-__-===-__-=======-__
|
||||
_( _)
|
||||
OO( )
|
||||
. o '===-______-===-____-==-__-====='
|
||||
.o
|
||||
. ______ _______________
|
||||
_()_||__|| __o^o___ | [] [] [] [] |
|
||||
( | | | |o
|
||||
/-OO----OO""="OO--OO"="OO---------OO"
|
||||
############################################################
|
||||
```
|
||||
[](geo/schweiz_ex.png?raw=true)
|
||||
*Left: station-to-station path of a single train through Switzerland obtained from official schedule data. Right: path of the same train map-matched by pfaedle.*
|
||||
|
||||
[](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.*
|
||||
|
||||
[](https://github.com/ad-freiburg/pfaedle/actions/workflows/build.yml)
|
||||
|
||||
# pfaedle
|
||||
|
||||
Precise map-matching for public transit feeds.
|
||||
Precise OpenStreetMap (OSM) map-matching for public transit schedules ([GTFS](https://developers.google.com/transit/gtfs/reference/) data).
|
||||
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`
|
||||
* `libboost-system`, `libboost-filesystem`, `libboost-geometry`
|
||||
* `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 -c <CFG FILE> -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`.
|
||||
|
||||
A default configuration file `pfaedle.cfg` can be found in this repo.
|
||||
|
||||
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` flag) shapes for the GTFS dataset for Freiburg like this:
|
||||
|
||||
```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 only for a specific mot, use the `-m` option. Possible
|
||||
values are either `tram`, `bus`, `rail`, `subway`, `ferry`, `funicular`,
|
||||
`gondola`, `all`.
|
||||
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 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).
|
||||
|
||||
|
|
@ -67,12 +73,30 @@ Multiple values can be specified (comma separated).
|
|||
|
||||
`pfaedle` comes with the ability to filter OpenStreetMap data. If you specify
|
||||
the `-X` flag, `pfaedle` will filter the input OSM file and output a new OSM
|
||||
file which contains *exactly* the data needed to calculate the shapes for the
|
||||
file which contains exactly the data needed to calculate the shapes for the
|
||||
input GTFS feed and the input configuration.
|
||||
|
||||
This can be used to avoid parsing (for example) the entire world.osm on each
|
||||
This can be used to avoid parsing (for example) the entire `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:
|
||||
|
|
@ -80,39 +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
|
||||
|
||||
The main config file distributed with this repository is `pfaedle.cfg`. The
|
||||
config file has some comments which hopefully explain the meaning behind the
|
||||
parameters
|
||||
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
|
||||
```
|
||||
|
||||
*Note:* 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.
|
||||
|
||||
## 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
30
action.yml
Normal 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
52
cmake/FindLibZip.cmake
Normal 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)
|
||||
|
|
@ -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
6233
cpplint.py
vendored
File diff suppressed because it is too large
Load diff
3249
geo/pfaedle.qgs
3249
geo/pfaedle.qgs
File diff suppressed because it is too large
Load diff
BIN
geo/schweiz_ex.png
Normal file
BIN
geo/schweiz_ex.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
geo/schweiz_ex_res.png
Normal file
BIN
geo/schweiz_ex_res.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 489 KiB |
BIN
geo/stuttgart_ex.png
Normal file
BIN
geo/stuttgart_ex.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
BIN
geo/stuttgart_ex_res.png
Normal file
BIN
geo/stuttgart_ex_res.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 456 KiB |
1431
pfaedle.cfg
Normal file
1431
pfaedle.cfg
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,25 @@
|
|||
set(PFAEDLE_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
cpplint_add_subdirectory(pfaedle)
|
||||
|
||||
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 5a1f788dde8f334d40505268f71bcc473d1968d8
|
||||
Subproject commit ca166b3446d5bb8b5fb8c6f637ca3f9cb0a8ff3b
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 7c395530d3c5b2d5c147fdabaaeeb0d1babceb04
|
||||
Subproject commit d26d5794d396141905d71ecb8cd4f45e0120cba7
|
||||
|
|
@ -16,5 +16,16 @@ configure_file (
|
|||
add_executable(pfaedle ${pfaedle_main})
|
||||
add_library(pfaedle_dep ${pfaedle_SRC})
|
||||
|
||||
target_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)
|
||||
|
|
|
|||
34
src/pfaedle/Def.h
Normal file
34
src/pfaedle/Def.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#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"
|
||||
|
||||
#define __str_a(s) __str_b(s)
|
||||
#define __str_b(s) #s
|
||||
#define __str_c(s) s ## 1
|
||||
#define __str_d(s) __str_c(s)
|
||||
|
||||
#if !defined(PFDL_PREC) || (__str_d(PFDL_PREC) == 1)
|
||||
#undef PFDL_PREC
|
||||
#define PFDL_PREC double
|
||||
#endif
|
||||
|
||||
#define PFDL_PREC_STR __str_a(PFDL_PREC)
|
||||
|
||||
#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
|
||||
|
||||
#endif // PFAEDLE_DEF_H_
|
||||
|
|
@ -2,41 +2,92 @@
|
|||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#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 "ad/cppgtfs/gtfs/Feed.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 "util/Misc.h"
|
||||
#include "util/geo/output/GeoGraphJsonOutput.h"
|
||||
#include "util/geo/output/GeoJsonOutput.h"
|
||||
#include "util/json/JsonWriter.h"
|
||||
#include "util/json/Writer.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
using std::string;
|
||||
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 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;
|
||||
|
||||
std::string getMotStr(const MOTs& mots);
|
||||
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots);
|
||||
enum class RetCode {
|
||||
SUCCESS = 0,
|
||||
NO_INPUT_FEED = 1,
|
||||
MULT_FEEDS_NOT_ALWD = 2,
|
||||
TRIP_NOT_FOUND = 3,
|
||||
GTFS_PARSE_ERR = 4,
|
||||
NO_OSM_INPUT = 5,
|
||||
MOT_CFG_PARSE_ERR = 6,
|
||||
OSM_PARSE_ERR = 7,
|
||||
GTFS_WRITE_ERR = 8,
|
||||
NO_MOT_CFG = 9
|
||||
};
|
||||
|
||||
std::string 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) {
|
||||
|
|
@ -46,45 +97,107 @@ 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;
|
||||
|
||||
ConfigReader cr;
|
||||
cr.read(&cfg, argc, argv);
|
||||
|
||||
ad::cppgtfs::gtfs::Feed gtfs;
|
||||
std::vector<pfaedle::gtfs::Feed> gtfs(cfg.feedPaths.size());
|
||||
|
||||
motCfgReader.parse(cfg.configPaths);
|
||||
std::vector<std::string> cfgPaths = getCfgPaths(cfg);
|
||||
|
||||
try {
|
||||
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 && !cfg.writeOsmfilter) {
|
||||
std::cerr << "No OSM input file specified (-x), see --help." << std::endl;
|
||||
exit(static_cast<int>(RetCode::NO_OSM_INPUT));
|
||||
}
|
||||
|
||||
if (motCfgReader.getConfigs().size() == 0) {
|
||||
LOG(ERROR) << "No MOT configurations specified and no implicit "
|
||||
"configurations found, see --help.";
|
||||
exit(static_cast<int>(RetCode::NO_MOT_CFG));
|
||||
}
|
||||
|
||||
T_START(gtfsBuild);
|
||||
|
||||
if (cfg.feedPaths.size() == 1) {
|
||||
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
|
||||
ad::cppgtfs::Parser p;
|
||||
p.parse(>fs, cfg.feedPaths[0]);
|
||||
LOG(INFO) << "Done.";
|
||||
if (cfg.inPlace) cfg.outputPath = cfg.feedPaths[0];
|
||||
if (!cfg.writeOverpass && !cfg.writeOsmfilter)
|
||||
LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[0] << " ...";
|
||||
try {
|
||||
ad::cppgtfs::Parser p(cfg.feedPaths[0], false,
|
||||
cfg.parseAdditionalGTFSFields,
|
||||
cfg.verbosity ? gtfsWarnCb : 0);
|
||||
p.parse(>fs[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));
|
||||
}
|
||||
} else if (cfg.writeOsm.size() || cfg.writeOverpass) {
|
||||
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
|
||||
if (!cfg.writeOverpass && !cfg.writeOsmfilter)
|
||||
LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[i] << " ...";
|
||||
try {
|
||||
ad::cppgtfs::Parser p(cfg.feedPaths[i]);
|
||||
p.parse(>fs[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));
|
||||
}
|
||||
}
|
||||
} else if (cfg.feedPaths.size() > 1) {
|
||||
LOG(ERROR) << "Maximal one input feed allowed.";
|
||||
exit(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;
|
||||
ad::cppgtfs::gtfs::Trip* singleTrip = 0;
|
||||
pfaedle::gtfs::Trip* singleTrip = 0;
|
||||
|
||||
if (cfg.shapeTripId.size()) {
|
||||
singleTrip = gtfs.getTrips().get(cfg.shapeTripId);
|
||||
if (!cfg.feedPaths.size()) {
|
||||
std::cout << "No input feed specified, see --help" << std::endl;
|
||||
exit(static_cast<int>(RetCode::NO_INPUT_FEED));
|
||||
}
|
||||
singleTrip = gtfs[0].getTrips().get(cfg.shapeTripId);
|
||||
if (!singleTrip) {
|
||||
LOG(ERROR) << "Trip #" << cfg.shapeTripId << " not found.";
|
||||
exit(1);
|
||||
exit(static_cast<int>(RetCode::TRIP_NOT_FOUND));
|
||||
}
|
||||
}
|
||||
|
||||
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(2500);
|
||||
if (cfg.feedPaths.size()) {
|
||||
box = ShapeBuilder::getPaddedGtfsBox(>fs, 2500, cmdCfgMots,
|
||||
cfg.shapeTripId);
|
||||
BBoxIdx box(BOX_PADDING);
|
||||
|
||||
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
|
||||
ShapeBuilder::getGtfsBox(>fs[i], cmdCfgMots, cfg.shapeTripId, true,
|
||||
&box, maxSpeed, 0, cfg.verbosity);
|
||||
}
|
||||
OsmBuilder osmBuilder;
|
||||
std::vector<pfaedle::osm::OsmReadOpts> opts;
|
||||
|
|
@ -94,93 +207,317 @@ int main(int argc, char** argv) {
|
|||
opts.push_back(o.osmBuildOpts);
|
||||
}
|
||||
}
|
||||
osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
|
||||
exit(0);
|
||||
try {
|
||||
osmBuilder.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
|
||||
} 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));
|
||||
}
|
||||
exit(static_cast<int>(RetCode::SUCCESS));
|
||||
} else if (cfg.writeOverpass) {
|
||||
BBoxIdx box(BOX_PADDING);
|
||||
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
|
||||
ShapeBuilder::getGtfsBox(>fs[i], cmdCfgMots, cfg.shapeTripId, true,
|
||||
&box, maxSpeed, 0, cfg.verbosity);
|
||||
}
|
||||
OsmBuilder osmBuilder;
|
||||
std::vector<pfaedle::osm::OsmReadOpts> opts;
|
||||
for (const auto& o : motCfgReader.getConfigs()) {
|
||||
if (std::find_first_of(o.mots.begin(), o.mots.end(), cmdCfgMots.begin(),
|
||||
cmdCfgMots.end()) != o.mots.end()) {
|
||||
opts.push_back(o.osmBuildOpts);
|
||||
}
|
||||
}
|
||||
osmBuilder.overpassQryWrite(&std::cout, opts, box);
|
||||
exit(static_cast<int>(RetCode::SUCCESS));
|
||||
} else if (cfg.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()) {
|
||||
auto usedMots = getContMots(motCfg, cmdCfgMots);
|
||||
std::string filePost;
|
||||
auto usedMots = pfaedle::router::motISect(motCfg.mots, cmdCfgMots);
|
||||
if (!usedMots.size()) continue;
|
||||
if (singleTrip && !usedMots.count(singleTrip->getRoute()->getType()))
|
||||
continue;
|
||||
if (motCfgReader.getConfigs().size() > 1)
|
||||
filePost = getFileNameMotStr(usedMots);
|
||||
|
||||
std::string motStr = getMotStr(usedMots);
|
||||
LOG(INFO) << "Calculating shapes for mots " << motStr;
|
||||
std::string motStr = pfaedle::router::getMotStr(usedMots);
|
||||
LOG(INFO) << "Matching shapes for mots " << motStr;
|
||||
|
||||
ShapeBuilder shapeBuilder(>fs, cmdCfgMots, motCfg, &ecoll, cfg);
|
||||
try {
|
||||
pfaedle::router::FeedStops fStops =
|
||||
pfaedle::router::writeMotStops(>fs[0], usedMots, cfg.shapeTripId);
|
||||
|
||||
if (cfg.writeGraph) {
|
||||
LOG(INFO) << "Outputting graph.json...";
|
||||
util::geo::output::GeoGraphJsonOutput out;
|
||||
std::ofstream fstr(cfg.dbgOutputPath + "/graph.json");
|
||||
out.print(*shapeBuilder.getGraph(), fstr);
|
||||
fstr.close();
|
||||
}
|
||||
pfaedle::osm::Restrictor restr;
|
||||
pfaedle::trgraph::Graph graph;
|
||||
pfaedle::osm::OsmBuilder osmBuilder;
|
||||
|
||||
if (singleTrip) {
|
||||
LOG(INFO) << "Outputting path.json...";
|
||||
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
|
||||
util::geo::output::GeoJsonOutput o(pstr);
|
||||
pfaedle::osm::BBoxIdx box(BOX_PADDING);
|
||||
ShapeBuilder::getGtfsBox(
|
||||
>fs[0], usedMots, cfg.shapeTripId, cfg.dropShapes, &box,
|
||||
motCfg.osmBuildOpts.maxSpeed, &hopDists, cfg.verbosity);
|
||||
|
||||
auto l = shapeBuilder.shapeL(singleTrip);
|
||||
T_START(osmBuild);
|
||||
|
||||
if (singleTrip->getShape()) {
|
||||
auto orig = Collector::getWebMercLine(singleTrip->getShape(), -1, -1);
|
||||
o.print(orig, {{"ver", "old"}});
|
||||
if (fStops.size())
|
||||
osmBuilder.read(cfg.osmPath, motCfg.osmBuildOpts, &graph, box,
|
||||
cfg.gridSize, &restr);
|
||||
|
||||
tOsmBuild += T_STOP(osmBuild);
|
||||
graphDimensions[filePost].first = graph.getNds().size();
|
||||
|
||||
for (const auto& nd : graph.getNds()) {
|
||||
graphDimensions[filePost].second += nd->getAdjListOut().size();
|
||||
}
|
||||
|
||||
o.print(l, {{"ver", "new"}});
|
||||
o.flush();
|
||||
pstr.close();
|
||||
StatsimiClassifier* statsimiClassifier;
|
||||
|
||||
exit(0);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
pfaedle::netgraph::Graph ng;
|
||||
shapeBuilder.shape(&ng);
|
||||
Router* router = 0;
|
||||
|
||||
if (cfg.buildTransitGraph) {
|
||||
LOG(INFO) << "Outputting trgraph.json...";
|
||||
util::geo::output::GeoGraphJsonOutput out;
|
||||
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph.json");
|
||||
out.print(ng, fstr);
|
||||
fstr.close();
|
||||
if (motCfg.routingOpts.transPenMethod == "exp") {
|
||||
if (cfg.noAStar)
|
||||
router = new RouterImpl<ExpoTransWeightNoHeur>();
|
||||
else
|
||||
router = new RouterImpl<ExpoTransWeight>();
|
||||
} else if (motCfg.routingOpts.transPenMethod == "distdiff") {
|
||||
if (cfg.noAStar)
|
||||
router = new RouterImpl<DistDiffTransWeightNoHeur>();
|
||||
else
|
||||
router = new RouterImpl<DistDiffTransWeight>();
|
||||
} else if (motCfg.routingOpts.transPenMethod == "timenorm") {
|
||||
if (cfg.noAStar)
|
||||
router = new RouterImpl<NormDistrTransWeightNoHeur>();
|
||||
else
|
||||
router = new RouterImpl<NormDistrTransWeight>();
|
||||
} else {
|
||||
LOG(ERROR) << "Unknown routing method "
|
||||
<< motCfg.routingOpts.transPenMethod;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ShapeBuilder shapeBuilder(>fs[0], 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...";
|
||||
util::geo::output::GeoGraphJsonOutput out;
|
||||
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
std::ofstream fstr(cfg.dbgOutputPath + "/graph.json");
|
||||
out.print(*shapeBuilder.getGraph(), fstr);
|
||||
fstr.close();
|
||||
}
|
||||
|
||||
if (singleTrip) exit(static_cast<int>(RetCode::SUCCESS));
|
||||
|
||||
if (cfg.buildTransitGraph) {
|
||||
util::geo::output::GeoGraphJsonOutput out;
|
||||
LOG(INFO) << "Outputting trgraph-" + filePost + ".json...";
|
||||
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph-" + filePost +
|
||||
".json");
|
||||
out.print(ng, fstr);
|
||||
fstr.close();
|
||||
}
|
||||
} 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()) {
|
||||
LOG(INFO) << "Writing output GTFS...";
|
||||
ad::cppgtfs::Writer w;
|
||||
w.write(>fs, cfg.outputPath);
|
||||
try {
|
||||
LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ...";
|
||||
pfaedle::gtfs::Writer w;
|
||||
w.write(>fs[0], cfg.outputPath);
|
||||
} catch (const ad::cppgtfs::WriterException& ex) {
|
||||
LOG(ERROR) << "Could not write output GTFS feed, reason was:";
|
||||
std::cerr << ex.what() << std::endl;
|
||||
exit(static_cast<int>(RetCode::GTFS_WRITE_ERR));
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
return static_cast<int>(RetCode::SUCCESS);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string getMotStr(const MOTs& mots) {
|
||||
bool first = false;
|
||||
std::string getFileNameMotStr(const MOTs& mots) {
|
||||
MOTs tmp = mots;
|
||||
std::string motStr;
|
||||
for (const auto& mot : mots) {
|
||||
if (first) motStr += ", ";
|
||||
motStr += "<" + Route::getTypeString(mot) + ">";
|
||||
first = true;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots) {
|
||||
MOTs ret;
|
||||
for (const auto& mot : mots) {
|
||||
if (motCfg.mots.count(mot)) {
|
||||
ret.insert(mot);
|
||||
std::vector<std::string> getCfgPaths(const Config& cfg) {
|
||||
if (cfg.configPaths.size()) return cfg.configPaths;
|
||||
std::vector<std::string> ret;
|
||||
|
||||
// install prefix global configuration path, if available
|
||||
{
|
||||
auto path = std::string(INSTALL_PREFIX) + std::string(CFG_DIR) + "/" +
|
||||
"pfaedle" + "/" + CFG_FILE_NAME;
|
||||
std::ifstream is(path);
|
||||
|
||||
LOG(DEBUG) << "Testing for config file at " << path;
|
||||
if (is.good()) {
|
||||
ret.push_back(path);
|
||||
LOG(DEBUG) << "Found implicit config file " << path;
|
||||
}
|
||||
}
|
||||
|
||||
// local user configuration path, if available
|
||||
{
|
||||
auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + "pfaedle" + "/" +
|
||||
CFG_FILE_NAME;
|
||||
std::ifstream is(path);
|
||||
|
||||
LOG(DEBUG) << "Testing for config file at " << path;
|
||||
if (is.good()) {
|
||||
ret.push_back(path);
|
||||
LOG(DEBUG) << "Found implicit config file " << path;
|
||||
}
|
||||
}
|
||||
|
||||
// free this here, as we use homedir in the block above
|
||||
|
||||
// CWD
|
||||
{
|
||||
char cwd[PATH_MAX];
|
||||
if (getcwd(cwd, sizeof(cwd))) {
|
||||
auto path = std::string(cwd) + "/" + CFG_FILE_NAME;
|
||||
std::ifstream is(path);
|
||||
|
||||
LOG(DEBUG) << "Testing for config file at " << path;
|
||||
if (is.good()) {
|
||||
ret.push_back(path);
|
||||
LOG(DEBUG) << "Found implicit config file " << path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,4 +7,7 @@
|
|||
// version number from cmake version module
|
||||
#define VERSION_FULL "@VERSION_GIT_FULL@"
|
||||
|
||||
// version number from cmake version module
|
||||
#define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
|
||||
|
||||
#endif // SRC_PFAEDLE_CONFIG_H_N
|
||||
|
|
|
|||
|
|
@ -7,78 +7,114 @@
|
|||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#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* COPY =
|
||||
"University of Freiburg - Chair of Algorithms and Data Structures";
|
||||
static const char* AUTHORS = "Patrick Brosi <brosi@informatik.uni-freiburg.de>";
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ConfigReader::help() {
|
||||
std::cout
|
||||
<< std::setfill(' ') << std::left
|
||||
<< "\033[1mpfaedle GTFS map matcher \033[22m\n"
|
||||
<< VERSION_FULL << " (built " << __DATE__ << " " << __TIME__ << ")\n\n"
|
||||
<< "(C) 2018 University of Freiburg\n"
|
||||
<< "Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>\n\n"
|
||||
<< "Usage: "
|
||||
<< " -x <OSM FILE> -c <CFG FILE> <GTFS FEED>\n\n"
|
||||
<< "Allowed options:\n\n"
|
||||
<< "General:\n"
|
||||
<< std::setw(35) << " -v [ --version ]"
|
||||
<< "print version\n"
|
||||
<< std::setw(35) << " -h [ --help ]"
|
||||
<< "show this help message\n"
|
||||
<< "\nInput:\n"
|
||||
<< std::setw(35) << " -c [ --config ] arg"
|
||||
<< "pfaedle config file\n"
|
||||
<< std::setw(35) << " -i [ --input ] arg"
|
||||
<< "gtfs feed(s), may also be given as positional parameter (see usage)\n"
|
||||
<< std::setw(35) << " -x [ --osm-file ] arg"
|
||||
<< "OSM xml input file\n"
|
||||
<< std::setw(35) << " -m [ --mots ] arg (=all)"
|
||||
<< "MOTs to calculate shapes for, comma separated, either as string "
|
||||
"{all,\n"
|
||||
<< std::setw(35) << " "
|
||||
<< "tram | streetcar, subway | metro, rail | train, bus, ferry | boat | "
|
||||
"\n"
|
||||
<< std::setw(35) << " "
|
||||
<< "ship, cableclar, gondola, funicular} or as GTFS mot codes\n"
|
||||
<< "\nOutput:\n"
|
||||
<< std::setw(35) << " -o [ --output ] arg (=gtfs-out)"
|
||||
<< "GTFS output path\n"
|
||||
<< std::setw(35) << " -X [ --osm-out ] arg"
|
||||
<< "if specified, a filtered OSM file will be written to <arg>\n"
|
||||
<< "\nDebug Output:\n"
|
||||
<< std::setw(35) << " -d [ --dbg-path ] arg (=geo)"
|
||||
<< "output path for debug files\n"
|
||||
<< std::setw(35) << " --write-trgraph"
|
||||
<< "write transit graph as GeoJSON to <dbg-path>/trgraph.json\n"
|
||||
<< std::setw(35) << " --write-graph"
|
||||
<< "write routing graph as GeoJSON to <dbg-path>/graph.json\n"
|
||||
<< std::setw(35) << " --write-cgraph"
|
||||
<< "write combination graph as GeoJSON to <dbg-path>/combraph.json\n"
|
||||
<< std::setw(35) << " --method arg (=global)"
|
||||
<< "matching method to use, either 'global' (based on HMM), 'greedy' or "
|
||||
"'greedy2'\n"
|
||||
<< std::setw(35) << " --eval"
|
||||
<< "evaluate existing shapes against matched 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 separated (e.g. 10,20,30,40)\n"
|
||||
<< "\nMisc:\n"
|
||||
<< std::setw(35) << " -T [ --trip-id ] arg"
|
||||
<< "Do routing only for trip <arg>, write result to\n"
|
||||
<< std::setw(35) << " "
|
||||
<< "<dbg-path>/path.json\n"
|
||||
<< std::setw(35) << " --grid-size arg (=2000)"
|
||||
<< "Grid cell size\n";
|
||||
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 <" << PFDL_PREC_STR << ">)\n\n"
|
||||
<< "(C) " << YEAR << " " << COPY << "\n"
|
||||
<< "Authors: " << AUTHORS << "\n\n"
|
||||
<< "Usage: " << bin << " -x <OSM FILE> <GTFS FEED>\n\n"
|
||||
<< "Allowed options:\n\n"
|
||||
<< "General:\n"
|
||||
<< std::setw(35) << " -v [ --version ]"
|
||||
<< "print version\n"
|
||||
<< std::setw(35) << " -h [ --help ]"
|
||||
<< "show this help message\n"
|
||||
<< std::setw(35) << " -D [ --drop-shapes ]"
|
||||
<< "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"
|
||||
<< std::setw(35) << " -i [ --input ] arg"
|
||||
<< "gtfs feed(s), may also be given as positional\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " parameter (see usage)\n"
|
||||
<< std::setw(35) << " -x [ --osm-file ] arg"
|
||||
<< "OSM xml input file\n"
|
||||
<< std::setw(35) << " -m [ --mots ] arg (=all)"
|
||||
<< "MOTs to calculate shapes for, comma sep.,\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " either as string "
|
||||
"{all, tram | streetcar,\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " subway | metro, rail | train, bus,\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " ferry | boat | ship, cablecar, gondola,\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " 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"
|
||||
<< std::setw(35) << " -X [ --osm-out ] arg"
|
||||
<< "if specified, a filtered OSM file will be\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " written to <arg>\n"
|
||||
<< std::setw(35) << " --inplace"
|
||||
<< "overwrite input GTFS feed with output feed\n"
|
||||
<< "\nDebug Output:\n"
|
||||
<< std::setw(35) << " -d [ --dbg-path ] arg (=.)"
|
||||
<< "output path for debug files\n"
|
||||
<< std::setw(35) << " --write-trgraph"
|
||||
<< "write transit graph as GeoJSON to\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " <dbg-path>/trgraph.json\n"
|
||||
<< std::setw(35) << " --write-graph"
|
||||
<< "write routing graph as GeoJSON to\n"
|
||||
<< std::setw(35) << " "
|
||||
<< " <dbg-path>/graph.json\n"
|
||||
<< "\nMisc:\n"
|
||||
<< std::setw(35) << " -T [ --trip-id ] arg"
|
||||
<< "Do routing only for trip <arg>, write result \n"
|
||||
<< std::setw(35) << " "
|
||||
<< " to <dbg-path>/path.json\n"
|
||||
<< std::setw(35) << " --overpass"
|
||||
<< "Output overpass query for matching OSM data\n"
|
||||
<< std::setw(35) << " --osmfilter"
|
||||
<< "Output osmfilter filter rules for matching OSM data\n"
|
||||
<< std::setw(35) << " --grid-size arg (=2000)"
|
||||
<< "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";
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
|
@ -93,44 +129,45 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
|
|||
{"drop-shapes", required_argument, 0, 'D'},
|
||||
{"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},
|
||||
{"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;
|
||||
case 12:
|
||||
cfg->noTrie = true;
|
||||
break;
|
||||
case 'o':
|
||||
cfg->outputPath = optarg;
|
||||
|
|
@ -151,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;
|
||||
|
|
@ -159,18 +196,47 @@ 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 << VERSION_FULL << " (built " << __DATE__ << " " << __TIME__
|
||||
<< ")\n\n";
|
||||
std::cout << "pfaedle " << VERSION_FULL << std::endl;
|
||||
exit(0);
|
||||
case 'p':
|
||||
printOpts = true;
|
||||
break;
|
||||
case 'h':
|
||||
help();
|
||||
help(argv[0]);
|
||||
exit(0);
|
||||
case ':':
|
||||
std::cerr << argv[optind - 1];
|
||||
|
|
@ -192,7 +258,8 @@ void ConfigReader::read(Config* cfg, int argc, char** argv) {
|
|||
|
||||
auto v = util::split(motStr, ',');
|
||||
for (const auto& motStr : v) {
|
||||
const auto& mots = Route::getTypesFromString(util::trim(motStr));
|
||||
const auto& mots =
|
||||
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(util::trim(motStr));
|
||||
cfg->mots.insert(mots.begin(), mots.end());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace config {
|
|||
class ConfigReader {
|
||||
public:
|
||||
static void read(Config* targetConfig, int argc, char** argv);
|
||||
static void help();
|
||||
static void help(const char* bin);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,19 @@
|
|||
#ifndef PFAEDLE_CONFIG_MOTCONFIG_H_
|
||||
#define PFAEDLE_CONFIG_MOTCONFIG_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "pfaedle/osm/OsmBuilder.h"
|
||||
#include "pfaedle/router/Router.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace config {
|
||||
|
||||
|
||||
struct MotConfig {
|
||||
router::MOTs mots;
|
||||
osm::OsmReadOpts osmBuildOpts;
|
||||
router::RoutingOpts routingOpts;
|
||||
std::string transWeight;
|
||||
};
|
||||
|
||||
inline bool operator==(const MotConfig& a, const MotConfig& b) {
|
||||
|
|
|
|||
|
|
@ -2,362 +2,643 @@
|
|||
// 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
|
||||
for (const auto& s : paths) {
|
||||
ConfigFileParser p;
|
||||
LOG(DEBUG) << "Reading config file " << s;
|
||||
p.parse(s);
|
||||
}
|
||||
|
||||
for (const auto& sec : p.getSecs()) {
|
||||
MotConfig curCfg;
|
||||
std::string secStr = sec.first;
|
||||
if (literal.size()) p.parseStr(literal);
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_keep")) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_keep", ' ')) {
|
||||
for (const auto& sec : p.getSecs()) {
|
||||
MotConfig cfg;
|
||||
|
||||
cfg.transWeight = "expo";
|
||||
|
||||
std::string secStr = sec.first;
|
||||
if (secStr.empty()) continue;
|
||||
|
||||
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")) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_keep", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
std::string name = std::string("osm_filter_lvl") + std::to_string(i);
|
||||
if (p.hasKey(secStr, name)) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
|
||||
cfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
std::string name =
|
||||
std::string("osm_filter_lvl") + std::to_string(i + 1);
|
||||
if (p.hasKey(secStr, name)) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
if (p.hasKey(secStr, "osm_filter_drop")) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.dropFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_snap_level")) {
|
||||
cfg.osmBuildOpts.maxSnapLevel = p.getInt(sec.first, "osm_max_snap_level");
|
||||
} else {
|
||||
cfg.osmBuildOpts.maxSnapLevel = 7;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_nohup")) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_nohup", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_oneway")) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_oneway", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_oneway_reverse")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_undirected")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_undirected", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_station")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_station", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_station_blocker")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
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")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_node_negative_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_no_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
cfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_station_name_attrs")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) {
|
||||
cfg.osmBuildOpts.statAttrRules.nameRule.push_back(getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_track_number_tags")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
|
||||
cfg.osmBuildOpts.statAttrRules.platformRule.push_back(
|
||||
getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_station_id_attrs")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_station_id_attrs", ' ')) {
|
||||
cfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_edge_track_number_tags")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_edge_track_number_tags", ' ')) {
|
||||
cfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_station_group_attrs")) {
|
||||
LOG(WARN) << "Option osm_station_group_attrs has been removed.";
|
||||
}
|
||||
|
||||
// default value, to enable color writing on old configs
|
||||
cfg.osmBuildOpts.relLinerules.colorRule = {"colour", "color"};
|
||||
|
||||
if (p.hasKey(secStr, "osm_line_relation_tags")) {
|
||||
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")
|
||||
cfg.osmBuildOpts.relLinerules.fromNameRule = tags;
|
||||
else if (rule.first == "to_name")
|
||||
cfg.osmBuildOpts.relLinerules.toNameRule = tags;
|
||||
else if (rule.first == "line_name")
|
||||
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")) {
|
||||
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")) {
|
||||
LOG(WARN) << "Option osm_max_snap_fallback_distance has been removed.";
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_osm_station_distance")) {
|
||||
double ref = p.getDouble(secStr, "osm_max_osm_station_distance");
|
||||
cfg.osmBuildOpts.maxOsmStationDistances.push_back(ref);
|
||||
} else {
|
||||
cfg.osmBuildOpts.maxOsmStationDistances.push_back(15);
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
|
||||
cfg.osmBuildOpts.maxBlockDistance =
|
||||
p.getDouble(secStr, "osm_max_node_block_distance");
|
||||
} else {
|
||||
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)) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
std::string name =
|
||||
std::string("osm_lvl") + std::to_string(i) + "_avg_speed";
|
||||
if (p.hasKey(secStr, name)) {
|
||||
double v = p.getPosDouble(sec.first, name);
|
||||
cfg.osmBuildOpts.levelDefSpeed[i] = v * 0.2777; // store in m/s
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) {
|
||||
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, "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")) {
|
||||
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")) {
|
||||
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_platform_unmatched_penalty")) {
|
||||
cfg.routingOpts.platformUnmatchedPen =
|
||||
p.getPosDouble(secStr, "routing_platform_unmatched_penalty");
|
||||
} else {
|
||||
// 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")) {
|
||||
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 {
|
||||
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")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
|
||||
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,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">",
|
||||
p.getVal(secStr, "station_normalize_chain").file);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "track_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
|
||||
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,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">",
|
||||
p.getVal(secStr, "track_normalize_chain").file);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "line_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
|
||||
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,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">",
|
||||
p.getVal(secStr, "line_normalize_chain").file);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "station_id_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "station_id_normalize_chain", ';');
|
||||
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_id_normalize_chain").file);
|
||||
}
|
||||
}
|
||||
|
||||
// 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& exCfg : _cfgs) {
|
||||
if (cfg == exCfg) {
|
||||
for (auto mot :
|
||||
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr)) {
|
||||
exCfg.mots.insert(mot);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "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(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_snap_level")) {
|
||||
curCfg.osmBuildOpts.maxSnapLevel =
|
||||
p.getInt(sec.first, "osm_max_snap_level");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxSnapLevel = 7;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_nohup")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_nohup", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_oneway")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_oneway", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_oneway_reverse")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_undirected")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_undirected", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_station")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_station", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_station_blocker")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_node_positive_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_node_negative_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_no_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_station_name_attrs")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) {
|
||||
curCfg.osmBuildOpts.statAttrRules.nameRule.push_back(
|
||||
getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_track_number_tags")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
|
||||
curCfg.osmBuildOpts.statAttrRules.platformRule.push_back(
|
||||
getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_edge_track_number_tags")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_edge_track_number_tags", ' ')) {
|
||||
curCfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_station_group_attrs")) {
|
||||
auto arr = p.getStrArr(secStr, "osm_station_group_attrs", ' ');
|
||||
|
||||
for (const auto& ruleStr : arr) {
|
||||
auto deep = getDeepAttrRule(ruleStr);
|
||||
// TODO(patrick): getKv is misused here as a a=b parser
|
||||
auto attrD = getKv(deep.attr);
|
||||
deep.attr = attrD.first;
|
||||
double dist = atof(attrD.second.c_str());
|
||||
curCfg.osmBuildOpts.statGroupNAttrRules.push_back({deep, dist});
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_line_relation_tags")) {
|
||||
auto arr = p.getStrArr(secStr, "osm_line_relation_tags", ' ');
|
||||
|
||||
for (const auto& ruleStr : arr) {
|
||||
auto rule = getKv(ruleStr);
|
||||
auto tags = util::split(rule.second, ',');
|
||||
if (rule.first == "from_name")
|
||||
curCfg.osmBuildOpts.relLinerules.fromNameRule = tags;
|
||||
else if (rule.first == "to_name")
|
||||
curCfg.osmBuildOpts.relLinerules.toNameRule = tags;
|
||||
else if (rule.first == "line_name")
|
||||
curCfg.osmBuildOpts.relLinerules.sNameRule = tags;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_snap_distance")) {
|
||||
curCfg.osmBuildOpts.maxSnapDistances =
|
||||
p.getDoubleArr(secStr, "osm_max_snap_distance", ',');
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxSnapDistances.push_back(50);
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_snap_fallback_distance")) {
|
||||
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
|
||||
p.getDouble(secStr, "osm_max_snap_fallback_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
|
||||
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
|
||||
curCfg.osmBuildOpts.maxSnapDistances.end()) *
|
||||
2;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_group_search_distance")) {
|
||||
curCfg.osmBuildOpts.maxGroupSearchDistance =
|
||||
p.getDouble(secStr, "osm_max_group_search_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxGroupSearchDistance =
|
||||
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
|
||||
curCfg.osmBuildOpts.maxSnapDistances.end()) *
|
||||
4;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_osm_station_distance")) {
|
||||
curCfg.osmBuildOpts.maxOsmStationDistance =
|
||||
p.getDouble(secStr, "osm_max_osm_station_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxOsmStationDistance = 5;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
|
||||
curCfg.osmBuildOpts.maxBlockDistance =
|
||||
p.getDouble(secStr, "osm_max_node_block_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxBlockDistance =
|
||||
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
|
||||
curCfg.osmBuildOpts.maxSnapDistances.end()) /
|
||||
8;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
std::string name =
|
||||
std::string("routing_lvl") + std::to_string(i) + "_fac";
|
||||
if (p.hasKey(secStr, name)) {
|
||||
double v = p.getDouble(sec.first, name);
|
||||
curCfg.routingOpts.levelPunish[i] = v;
|
||||
} else {
|
||||
curCfg.routingOpts.levelPunish[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "routing_full_turn_punish")) {
|
||||
curCfg.routingOpts.fullTurnPunishFac =
|
||||
p.getDouble(secStr, "routing_full_turn_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_full_turn_angle")) {
|
||||
double ang = p.getDouble(secStr, "routing_full_turn_angle");
|
||||
curCfg.routingOpts.fullTurnAngle = ang;
|
||||
} else {
|
||||
curCfg.routingOpts.fullTurnAngle = 5;
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_snap_full_turn_angle")) {
|
||||
double ang = p.getDouble(secStr, "routing_snap_full_turn_angle");
|
||||
curCfg.osmBuildOpts.maxAngleSnapReach = ang;
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxAngleSnapReach =
|
||||
curCfg.routingOpts.fullTurnAngle;
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_pass_thru_station_punish")) {
|
||||
curCfg.routingOpts.passThruStationsPunish =
|
||||
p.getDouble(secStr, "routing_pass_thru_station_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) {
|
||||
curCfg.routingOpts.oneWayPunishFac =
|
||||
p.getDouble(secStr, "routing_one_way_meter_punish_fac");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_one_way_edge_punish")) {
|
||||
curCfg.routingOpts.oneWayEdgePunish =
|
||||
p.getDouble(secStr, "routing_one_way_edge_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) {
|
||||
curCfg.routingOpts.lineUnmatchedPunishFact =
|
||||
p.getDouble(secStr, "routing_line_unmatched_punish_fac");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_platform_unmatched_punish")) {
|
||||
curCfg.routingOpts.platformUnmatchedPen =
|
||||
p.getDouble(secStr, "routing_platform_unmatched_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_non_osm_station_punish")) {
|
||||
curCfg.routingOpts.nonOsmPen =
|
||||
p.getDouble(secStr, "routing_non_osm_station_punish");
|
||||
} else {
|
||||
curCfg.routingOpts.nonOsmPen = 0;
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_station_distance_punish_fac")) {
|
||||
curCfg.routingOpts.stationDistPenFactor =
|
||||
p.getDouble(secStr, "routing_station_distance_punish_fac");
|
||||
} else {
|
||||
curCfg.routingOpts.stationDistPenFactor = 1;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "station_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
|
||||
curCfg.osmBuildOpts.statNormzer =
|
||||
trgraph::Normalizer(getNormRules(arr));
|
||||
} catch (const std::exception& e) {
|
||||
throw ParseExc(p.getVal(secStr, "station_normalize_chain").line,
|
||||
p.getVal(secStr, "station_normalize_chain").pos,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">", s);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "track_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
|
||||
curCfg.osmBuildOpts.trackNormzer =
|
||||
trgraph::Normalizer(getNormRules(arr));
|
||||
} catch (const std::exception& e) {
|
||||
throw ParseExc(p.getVal(secStr, "track_normalize_chain").line,
|
||||
p.getVal(secStr, "station_normalize_chain").pos,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">", s);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "line_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
|
||||
curCfg.osmBuildOpts.lineNormzer =
|
||||
trgraph::Normalizer(getNormRules(arr));
|
||||
} catch (const std::exception& e) {
|
||||
throw ParseExc(p.getVal(secStr, "station_normalize_chain").line,
|
||||
p.getVal(secStr, "station_normalize_chain").pos,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">", s);
|
||||
}
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (auto& cfg : _cfgs) {
|
||||
if (cfg == curCfg) {
|
||||
for (auto mot : Route::getTypesFromString(secStr)) {
|
||||
cfg.mots.insert(mot);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
curCfg.mots = Route::getTypesFromString(secStr);
|
||||
_cfgs.push_back(curCfg);
|
||||
}
|
||||
if (!found) {
|
||||
cfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr);
|
||||
_cfgs.push_back(cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@
|
|||
#ifndef PFAEDLE_CONFIG_MOTCONFIGREADER_H_
|
||||
#define PFAEDLE_CONFIG_MOTCONFIGREADER_H_
|
||||
|
||||
#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"
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ using ad::cppgtfs::gtfs::Route;
|
|||
class MotConfigReader {
|
||||
public:
|
||||
MotConfigReader();
|
||||
void parse(const std::vector<std::string>& paths);
|
||||
void parse(const std::vector<std::string>& paths, const std::string& literal);
|
||||
|
||||
const std::vector<MotConfig>& getConfigs() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,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 {
|
||||
|
|
@ -18,34 +20,55 @@ using ad::cppgtfs::gtfs::Route;
|
|||
|
||||
struct Config {
|
||||
Config()
|
||||
: dbgOutputPath("geo"),
|
||||
: dbgOutputPath("."),
|
||||
solveMethod("global"),
|
||||
evalPath("."),
|
||||
outputPath("gtfs-out"),
|
||||
dropShapes(false),
|
||||
useHMM(false),
|
||||
writeGraph(false),
|
||||
writeCombGraph(false),
|
||||
evaluate(false),
|
||||
buildTransitGraph(false),
|
||||
gridSize(2000) {}
|
||||
useCaching(false),
|
||||
writeOverpass(false),
|
||||
writeOsmfilter(false),
|
||||
inPlace(false),
|
||||
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;
|
||||
|
|
@ -57,8 +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) {
|
||||
|
|
|
|||
|
|
@ -1,417 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <csignal>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/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::FLine;
|
||||
using util::geo::PolyLine;
|
||||
using util::geo::FPoint;
|
||||
using ad::cppgtfs::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;
|
||||
FLine oldL = getWebMercLine(
|
||||
oldS, t->getStopTimes().begin()->getShapeDistanceTravelled(),
|
||||
(--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists);
|
||||
|
||||
std::vector<double> newDists;
|
||||
FLine 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<float> oldStart = oldSegs[0];
|
||||
PolyLine<float> 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<float> oldEnd = oldSegs[oldSegs.size() - 1];
|
||||
PolyLine<float> 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
|
||||
FLine oldLCut;
|
||||
FLine newLCut;
|
||||
|
||||
for (auto oldL : oldSegs) {
|
||||
gjout.print(oldL, {{"ver", "old"}});
|
||||
oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end());
|
||||
}
|
||||
for (auto newL : newSegs) {
|
||||
gjout.print(newL, {{"ver", "new"}});
|
||||
newLCut.insert(newLCut.end(), newL.begin(), newL.end());
|
||||
}
|
||||
|
||||
gjout.flush();
|
||||
fstr.close();
|
||||
|
||||
double fac = cos(2 * atan(exp((oldSegs.front().front().get<1>() +
|
||||
oldSegs.back().back().get<1>()) /
|
||||
6378137.0)) -
|
||||
1.5707965);
|
||||
|
||||
if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS)) {
|
||||
fd = _dCache[oldS][newS];
|
||||
} else {
|
||||
fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac;
|
||||
_dCache[oldS][newS] = fd;
|
||||
}
|
||||
|
||||
if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS)) {
|
||||
unmatchedSegments = _dACache[oldS][newS].first;
|
||||
unmatchedSegmentsLength = _dACache[oldS][newS].second;
|
||||
} else {
|
||||
auto dA = getDa(oldSegs, newSegs);
|
||||
_dACache[oldS][newS] = 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<FLine> Collector::segmentize(
|
||||
const Trip* t, const FLine& shape, const std::vector<double>& dists,
|
||||
const std::vector<double>* newTripDists) {
|
||||
std::vector<FLine> ret;
|
||||
|
||||
if (t->getStopTimes().size() < 2) return ret;
|
||||
|
||||
util::geo::PolyLine<float> pl(shape);
|
||||
std::vector<std::pair<FPoint, double> > cuts;
|
||||
|
||||
size_t i = 0;
|
||||
for (auto st : t->getStopTimes()) {
|
||||
if (newTripDists) {
|
||||
cuts.push_back(std::pair<FPoint, double>(
|
||||
util::geo::latLngToWebMerc<float>(st.getStop()->getLat(),
|
||||
st.getStop()->getLng()),
|
||||
(*newTripDists)[i]));
|
||||
} else {
|
||||
cuts.push_back(std::pair<FPoint, double>(
|
||||
util::geo::latLngToWebMerc<float>(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();
|
||||
util::geo::PolyLine<float> l(
|
||||
FLine(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();
|
||||
}
|
||||
|
||||
util::geo::PolyLine<float> beforePl(
|
||||
FLine(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;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FLine Collector::getWebMercLine(const Shape* s, double from, double t) {
|
||||
return getWebMercLine(s, from, t, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FLine Collector::getWebMercLine(const Shape* s, double from, double to,
|
||||
std::vector<double>* dists) {
|
||||
FLine 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;
|
||||
|
||||
FPoint mercP = util::geo::latLngToWebMerc<float>(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<FLine>& a,
|
||||
const std::vector<FLine>& 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().get<1>() + a.back().back().get<1>()) /
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,96 +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/eval/Result.h"
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
using ad::cppgtfs::gtfs::Shape;
|
||||
using util::geo::FLine;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace eval {
|
||||
|
||||
/*
|
||||
* Collects routing results for evaluation
|
||||
*/
|
||||
class Collector {
|
||||
public:
|
||||
Collector(const std::string& evalOutPath, const std::vector<double>& dfBins)
|
||||
: _noOrigShp(0),
|
||||
_noMatchShp(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 FLine getWebMercLine(const Shape* s, double from, double to);
|
||||
static FLine 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<const Shape*, double> > _dCache;
|
||||
std::map<const Shape*, std::map<const Shape*, std::pair<size_t, double> > >
|
||||
_dACache;
|
||||
size_t _noOrigShp;
|
||||
size_t _noMatchShp;
|
||||
|
||||
double _fdSum;
|
||||
size_t _unmatchedSegSum;
|
||||
double _unmatchedSegLengthSum;
|
||||
|
||||
std::string _evalOutPath;
|
||||
|
||||
std::vector<double> _dfBins;
|
||||
|
||||
static std::pair<size_t, double> getDa(const std::vector<FLine>& a,
|
||||
const std::vector<FLine>& b);
|
||||
|
||||
static std::vector<FLine> segmentize(const Trip* t, const FLine& 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_
|
||||
40
src/pfaedle/gtfs/Feed.h
Normal file
40
src/pfaedle/gtfs/Feed.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_GTFS_FEED_H_
|
||||
#define PFAEDLE_GTFS_FEED_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Service.h"
|
||||
#include "ShapeContainer.h"
|
||||
#include "StopTime.h"
|
||||
#include "ad/cppgtfs/gtfs/ContContainer.h"
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "ad/cppgtfs/gtfs/NullContainer.h"
|
||||
#include "ad/cppgtfs/gtfs/Stop.h"
|
||||
#include "ad/cppgtfs/gtfs/StopTime.h"
|
||||
#include "ad/cppgtfs/gtfs/Trip.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace gtfs {
|
||||
|
||||
typedef ad::cppgtfs::gtfs::FeedB<
|
||||
ad::cppgtfs::gtfs::Agency, 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,
|
||||
ad::cppgtfs::gtfs::Route, Shape>
|
||||
Trip;
|
||||
|
||||
} // namespace gtfs
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_GTFS_FEED_H_
|
||||
43
src/pfaedle/gtfs/Service.h
Normal file
43
src/pfaedle/gtfs/Service.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_GTFS_SERVICE_H_
|
||||
#define PFAEDLE_GTFS_SERVICE_H_
|
||||
|
||||
#include <string>
|
||||
#include "ad/cppgtfs/gtfs/Service.h"
|
||||
#include "util/Misc.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace gtfs {
|
||||
|
||||
class Service {
|
||||
public:
|
||||
typedef std::string Ref;
|
||||
static std::string getId(Ref r) { return r; }
|
||||
|
||||
explicit Service(const string& id) : _id(id) {}
|
||||
Service(const string& id, uint8_t serviceDays,
|
||||
ad::cppgtfs::gtfs::ServiceDate start,
|
||||
ad::cppgtfs::gtfs::ServiceDate end)
|
||||
: _id(id) {
|
||||
UNUSED(serviceDays);
|
||||
UNUSED(start);
|
||||
UNUSED(end);
|
||||
}
|
||||
|
||||
const std::string& getId() const { return _id; }
|
||||
void addException(const ad::cppgtfs::gtfs::ServiceDate& d,
|
||||
ad::cppgtfs::gtfs::Service::EXCEPTION_TYPE t) {
|
||||
UNUSED(d);
|
||||
UNUSED(t);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _id;
|
||||
};
|
||||
} // namespace gtfs
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_GTFS_SERVICE_H_
|
||||
72
src/pfaedle/gtfs/ShapeContainer.h
Normal file
72
src/pfaedle/gtfs/ShapeContainer.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_GTFS_SHAPECONTAINER_H_
|
||||
#define PFAEDLE_GTFS_SHAPECONTAINER_H_
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "ad/cppgtfs/gtfs/Shape.h"
|
||||
#include "ad/cppgtfs/gtfs/flat/Shape.h"
|
||||
#include "pfaedle/Def.h"
|
||||
#include "util/Misc.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace gtfs {
|
||||
|
||||
struct Shape {
|
||||
explicit Shape(const std::string& id) : id(id) {}
|
||||
typedef std::string Ref;
|
||||
static std::string getId(Ref r) { return r; }
|
||||
|
||||
template <typename T>
|
||||
bool addPoint(T p) {
|
||||
UNUSED(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& getId() const { return id; }
|
||||
|
||||
std::string id;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ShapeContainer {
|
||||
public:
|
||||
ShapeContainer();
|
||||
~ShapeContainer();
|
||||
T* add(const T& obj);
|
||||
bool remove(const std::string& id);
|
||||
const T* get(const std::string& id) const;
|
||||
T* get(const std::string& id);
|
||||
const std::string getRef(const std::string& id) const;
|
||||
std::string getRef(const std::string& id);
|
||||
size_t size() const;
|
||||
void finalize() {}
|
||||
bool has(const std::string& id) const;
|
||||
|
||||
std::string add(const ad::cppgtfs::gtfs::Shape& s);
|
||||
void open();
|
||||
bool nextStoragePt(ad::cppgtfs::gtfs::flat::ShapePoint* ret);
|
||||
|
||||
private:
|
||||
std::set<std::string> _ids;
|
||||
std::fstream _storage;
|
||||
size_t _ptr;
|
||||
size_t _max;
|
||||
std::string _curId;
|
||||
std::stringstream _writeBuffer;
|
||||
std::fpos<std::mbstate_t> _lastBuff;
|
||||
};
|
||||
|
||||
#include "ShapeContainer.tpp"
|
||||
|
||||
} // namespace gtfs
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_GTFS_SHAPECONTAINER_H_
|
||||
158
src/pfaedle/gtfs/ShapeContainer.tpp
Normal file
158
src/pfaedle/gtfs/ShapeContainer.tpp
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
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
|
||||
unlink(f.c_str());
|
||||
|
||||
if (!_storage.good()) {
|
||||
std::cerr << "Could not open temporary file " << f << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
ShapeContainer<T>::~ShapeContainer() {
|
||||
_storage << _writeBuffer.rdbuf();
|
||||
_storage.flush();
|
||||
_storage.close();
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
T* ShapeContainer<T>::add(const T& ent) {
|
||||
_ids.insert(ent.getId());
|
||||
return reinterpret_cast<T*>(1);
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
bool ShapeContainer<T>::remove(const std::string& id) {
|
||||
_ids.erase(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
T* ShapeContainer<T>::get(const std::string& id) {
|
||||
UNUSED(id);
|
||||
return reinterpret_cast<T*>(0);
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
const T* ShapeContainer<T>::get(const std::string& id) const {
|
||||
UNUSED(id);
|
||||
return reinterpret_cast<T*>(0);
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
bool ShapeContainer<T>::has(const std::string& id) const {
|
||||
return _ids.count(id);
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
size_t ShapeContainer<T>::size() const {
|
||||
return _ids.size();
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::string ShapeContainer<T>::add(const ad::cppgtfs::gtfs::Shape& s) {
|
||||
if (has(s.getId())) return s.getId();
|
||||
size_t size = s.getPoints().size();
|
||||
_ids.insert(s.getId());
|
||||
|
||||
_writeBuffer << s.getId();
|
||||
_writeBuffer.put(' ');
|
||||
_writeBuffer.write(reinterpret_cast<const char*>(&size), sizeof(size));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return s.getId();
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
void ShapeContainer<T>::open() {
|
||||
_storage << _writeBuffer.rdbuf();
|
||||
_storage.flush();
|
||||
_writeBuffer.clear();
|
||||
|
||||
_ptr = 0;
|
||||
_max = 0;
|
||||
_storage.clear();
|
||||
_storage.seekg(0, std::ios::beg);
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
bool ShapeContainer<T>::nextStoragePt(
|
||||
ad::cppgtfs::gtfs::flat::ShapePoint* ret) {
|
||||
while (_storage.good() && !_storage.fail()) {
|
||||
if (!_ptr) {
|
||||
_storage >> _curId;
|
||||
_storage.ignore();
|
||||
|
||||
_storage.read(reinterpret_cast<char*>(&_max), sizeof(_max));
|
||||
}
|
||||
|
||||
if (!_storage.good() || _storage.fail()) return false;
|
||||
|
||||
_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;
|
||||
|
||||
if (_ptr + 1 == _max)
|
||||
_ptr = 0;
|
||||
else
|
||||
_ptr++;
|
||||
|
||||
if (!_storage.good() || _storage.fail()) return false;
|
||||
|
||||
if (has(ret->id)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
const std::string ShapeContainer<T>::getRef(const std::string& id) const {
|
||||
if (!has(id)) return "";
|
||||
return id;
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::string ShapeContainer<T>::getRef(const std::string& id) {
|
||||
if (!has(id)) return "";
|
||||
return id;
|
||||
}
|
||||
74
src/pfaedle/gtfs/StopTime.h
Normal file
74
src/pfaedle/gtfs/StopTime.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_GTFS_STOPTIME_H_
|
||||
#define PFAEDLE_GTFS_STOPTIME_H_
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/gtfs/Stop.h"
|
||||
#include "ad/cppgtfs/gtfs/StopTime.h"
|
||||
#include "util/Misc.h"
|
||||
|
||||
using std::exception;
|
||||
using std::string;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace gtfs {
|
||||
|
||||
template <typename StopT>
|
||||
class StopTime {
|
||||
public:
|
||||
StopTime(const ad::cppgtfs::gtfs::Time& at, const ad::cppgtfs::gtfs::Time& dt,
|
||||
typename StopT::Ref s, uint32_t seq, const std::string& hs,
|
||||
ad::cppgtfs::gtfs::flat::StopTime::PU_DO_TYPE put,
|
||||
ad::cppgtfs::gtfs::flat::StopTime::PU_DO_TYPE dot, float distTrav,
|
||||
bool isTp, 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(continuousDropOff);
|
||||
UNUSED(continuousPickup);
|
||||
}
|
||||
|
||||
const typename StopT::Ref getStop() const { return _s; }
|
||||
typename StopT::Ref getStop() { return _s; }
|
||||
void setShapeDistanceTravelled(double d) { _dist = d; }
|
||||
|
||||
ad::cppgtfs::gtfs::Time getArrivalTime() const {
|
||||
return _at;
|
||||
}
|
||||
ad::cppgtfs::gtfs::Time getDepartureTime() const {
|
||||
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>
|
||||
struct StopTimeCompare {
|
||||
bool operator()(const StopTimeT& lh, const StopTimeT& rh) const {
|
||||
return lh.getSeq() < rh.getSeq();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gtfs
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_GTFS_STOPTIME_H_
|
||||
723
src/pfaedle/gtfs/Writer.cpp
Normal file
723
src/pfaedle/gtfs/Writer.cpp
Normal file
|
|
@ -0,0 +1,723 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// 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::cppgtfs::Parser;
|
||||
using ad::util::CsvWriter;
|
||||
#ifdef LIBZIP_FOUND
|
||||
using ad::util::ZipCsvParser;
|
||||
#endif
|
||||
using pfaedle::gtfs::Writer;
|
||||
using util::getTmpFName;
|
||||
|
||||
// ____________________________________________________________________________
|
||||
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::string gtfsPath(path);
|
||||
std::string curFile;
|
||||
std::string curFileTg;
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
if (toZip) {
|
||||
#ifdef LIBZIP_FOUND
|
||||
moveIntoZip(za, curFile, "calendar.txt");
|
||||
#endif
|
||||
} else {
|
||||
if (std::rename(curFile.c_str(), curFileTg.c_str()))
|
||||
cannotWrite(curFileTg);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
if (toZip) {
|
||||
#ifdef LIBZIP_FOUND
|
||||
moveIntoZip(za, curFile, "calendar_dates.txt");
|
||||
#endif
|
||||
} else {
|
||||
if (std::rename(curFile.c_str(), curFileTg.c_str()))
|
||||
cannotWrite(curFileTg);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
if (toZip) {
|
||||
#ifdef LIBZIP_FOUND
|
||||
moveIntoZip(za, curFile, "transfers.txt");
|
||||
#endif
|
||||
} else {
|
||||
if (std::rename(curFile.c_str(), curFileTg.c_str()))
|
||||
cannotWrite(curFileTg);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
if (toZip) {
|
||||
#ifdef LIBZIP_FOUND
|
||||
moveIntoZip(za, curFile, "fare_attributes.txt");
|
||||
#endif
|
||||
} else {
|
||||
if (std::rename(curFile.c_str(), curFileTg.c_str()))
|
||||
cannotWrite(curFileTg);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
if (toZip) {
|
||||
#ifdef LIBZIP_FOUND
|
||||
moveIntoZip(za, curFile, "fare_rules.txt");
|
||||
#endif
|
||||
} else {
|
||||
if (std::rename(curFile.c_str(), curFileTg.c_str()))
|
||||
cannotWrite(curFileTg);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
if (toZip) {
|
||||
#ifdef LIBZIP_FOUND
|
||||
moveIntoZip(za, curFile, "frequencies.txt");
|
||||
#endif
|
||||
} else {
|
||||
if (std::rename(curFile.c_str(), curFileTg.c_str()))
|
||||
cannotWrite(curFileTg);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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(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();
|
||||
|
||||
if (toZip) {
|
||||
#ifdef LIBZIP_FOUND
|
||||
moveIntoZip(za, curFile, "feed_info.txt");
|
||||
#endif
|
||||
} else {
|
||||
if (std::rename(curFile.c_str(), curFileTg.c_str()))
|
||||
cannotWrite(curFileTg);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
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());
|
||||
if (!f->getStartDate().empty())
|
||||
csvw->writeInt(f->getStartDate().getYYYYMMDD());
|
||||
else
|
||||
csvw->skip();
|
||||
if (!f->getEndDate().empty())
|
||||
csvw->writeInt(f->getEndDate().getYYYYMMDD());
|
||||
else
|
||||
csvw->skip();
|
||||
csvw->writeString(f->getVersion());
|
||||
csvw->writeString(f->getContactEmail());
|
||||
csvw->writeString(f->getContactUrl());
|
||||
csvw->writeString(f->getDefaultLang());
|
||||
csvw->flushLine();
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
void Writer::writePathways(gtfs::Feed* sourceFeed, std::ostream* os) const {
|
||||
Parser p(sourceFeed->getPath());
|
||||
auto csvp = p.getCsvParser("pathways.txt");
|
||||
ad::cppgtfs::Writer w;
|
||||
|
||||
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.get());
|
||||
|
||||
while (p.nextAgency(csvp.get(), &fa, flds)) {
|
||||
w.writeAgency(fa, csvw.get(), sourceFeed->getAgencyAddFlds());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
void Writer::writeStops(gtfs::Feed* sourceFeed, std::ostream* os) const {
|
||||
Parser p(sourceFeed->getPath());
|
||||
auto csvp = p.getCsvParser("stops.txt");
|
||||
ad::cppgtfs::Writer w;
|
||||
|
||||
auto csvw =
|
||||
ad::cppgtfs::Writer::getStopsCsvw(os, sourceFeed->getStopAddFlds());
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::Stop s;
|
||||
auto flds = Parser::getStopFlds(csvp.get());
|
||||
|
||||
while (p.nextStop(csvp.get(), &s, flds)) {
|
||||
w.writeStop(s, csvw.get(), sourceFeed->getStopAddFlds());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
void Writer::writeRoutes(gtfs::Feed* sourceFeed, std::ostream* os) const {
|
||||
ad::cppgtfs::Writer w;
|
||||
|
||||
auto csvw =
|
||||
ad::cppgtfs::Writer::getRoutesCsvw(os, sourceFeed->getRouteAddFlds());
|
||||
csvw->flushLine();
|
||||
|
||||
for (auto r : sourceFeed->getRoutes()) {
|
||||
w.writeRoute(r.second->getFlat(), csvw.get(),
|
||||
sourceFeed->getRouteAddFlds());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
void Writer::writeCalendar(gtfs::Feed* sourceFeed, std::ostream* os) const {
|
||||
Parser p(sourceFeed->getPath());
|
||||
auto csvp = p.getCsvParser("calendar.txt");
|
||||
ad::cppgtfs::Writer w;
|
||||
|
||||
auto csvw = ad::cppgtfs::Writer::getCalendarCsvw(os);
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::Calendar c;
|
||||
auto flds = Parser::getCalendarFlds(csvp.get());
|
||||
|
||||
while (p.nextCalendar(csvp.get(), &c, flds)) {
|
||||
w.writeCalendar(c, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
void Writer::writeCalendarDates(gtfs::Feed* sourceFeed,
|
||||
std::ostream* os) const {
|
||||
Parser p(sourceFeed->getPath());
|
||||
auto csvp = p.getCsvParser("calendar_dates.txt");
|
||||
ad::cppgtfs::Writer w;
|
||||
|
||||
auto csvw = ad::cppgtfs::Writer::getCalendarDatesCsvw(os);
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::CalendarDate c;
|
||||
auto flds = Parser::getCalendarDateFlds(csvp.get());
|
||||
|
||||
while (p.nextCalendarDate(csvp.get(), &c, flds)) {
|
||||
w.writeCalendarDate(c, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
void Writer::writeFrequencies(gtfs::Feed* sourceFeed, std::ostream* os) const {
|
||||
Parser p(sourceFeed->getPath());
|
||||
auto csvp = p.getCsvParser("frequencies.txt");
|
||||
ad::cppgtfs::Writer w;
|
||||
|
||||
auto csvw = ad::cppgtfs::Writer::getFrequencyCsvw(os);
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::Frequency f;
|
||||
auto flds = Parser::getFrequencyFlds(csvp.get());
|
||||
|
||||
while (p.nextFrequency(csvp.get(), &f, flds)) {
|
||||
w.writeFrequency(f, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
void Writer::writeTransfers(gtfs::Feed* sourceFeed, std::ostream* os) const {
|
||||
Parser p(sourceFeed->getPath());
|
||||
auto csvp = p.getCsvParser("transfers.txt");
|
||||
ad::cppgtfs::Writer w;
|
||||
|
||||
auto csvw = ad::cppgtfs::Writer::getTransfersCsvw(os);
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::Transfer t;
|
||||
auto flds = Parser::getTransfersFlds(csvp.get());
|
||||
|
||||
while (p.nextTransfer(csvp.get(), &t, flds)) {
|
||||
w.writeTransfer(t, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
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;
|
||||
|
||||
auto csvw = ad::cppgtfs::Writer::getFaresCsvw(os);
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::Fare f;
|
||||
auto flds = Parser::getFareFlds(csvp.get());
|
||||
|
||||
while (p.nextFare(csvp.get(), &f, flds)) {
|
||||
w.writeFare(f, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
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;
|
||||
|
||||
auto csvw = ad::cppgtfs::Writer::getFareRulesCsvw(os);
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::FareRule f;
|
||||
auto flds = Parser::getFareRuleFlds(csvp.get());
|
||||
|
||||
while (p.nextFareRule(csvp.get(), &f, flds)) {
|
||||
w.writeFareRule(f, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
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;
|
||||
|
||||
Parser p(sourceFeed->getPath());
|
||||
auto csvp = p.getCsvParser("shapes.txt");
|
||||
|
||||
if (csvp->isGood()) {
|
||||
auto flds = Parser::getShapeFlds(csvp.get());
|
||||
|
||||
std::string curShapeId;
|
||||
std::string curSkipShapeId;
|
||||
|
||||
while (p.nextShapePoint(csvp.get(), &sp, flds)) {
|
||||
if (sp.id == curSkipShapeId) continue;
|
||||
if (sp.id != curShapeId) {
|
||||
if (sourceFeed->getShapes().has(sp.id)) {
|
||||
curShapeId = sp.id;
|
||||
} else {
|
||||
curSkipShapeId = sp.id;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
w.writeShapePoint(sp, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
sourceFeed->getShapes().open();
|
||||
while (sourceFeed->getShapes().nextStoragePt(&sp)) {
|
||||
w.writeShapePoint(sp, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
bool Writer::writeTrips(gtfs::Feed* sourceFeed, std::ostream* os) const {
|
||||
ad::cppgtfs::Writer w;
|
||||
bool hasFreqs = false;
|
||||
|
||||
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.get(), sourceFeed->getTripAddFlds());
|
||||
}
|
||||
|
||||
return hasFreqs;
|
||||
}
|
||||
|
||||
// ____________________________________________________________________________
|
||||
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;
|
||||
|
||||
auto csvw = ad::cppgtfs::Writer::getStopTimesCsvw(os);
|
||||
csvw->flushLine();
|
||||
|
||||
ad::cppgtfs::gtfs::flat::StopTime st;
|
||||
auto flds = Parser::getStopTimeFlds(csvp.get());
|
||||
|
||||
std::string curTripId;
|
||||
Trip* cur = 0;
|
||||
|
||||
while (p.nextStopTime(csvp.get(), &st, flds)) {
|
||||
// we may have changed to distance field
|
||||
if (curTripId != st.trip) {
|
||||
cur = sourceFeed->getTrips().get(st.trip);
|
||||
curTripId = st.trip;
|
||||
}
|
||||
for (const auto& stN : cur->getStopTimes()) {
|
||||
if (stN.getSeq() == st.sequence)
|
||||
st.shapeDistTravelled = stN.getShapeDistanceTravelled();
|
||||
}
|
||||
|
||||
w.writeStopTime(st, csvw.get());
|
||||
}
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
void Writer::cannotWrite(const std::string& file) {
|
||||
std::stringstream ss;
|
||||
ss << "Could not write to file";
|
||||
throw ad::cppgtfs::WriterException(ss.str(), file);
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
void Writer::cannotWrite(const std::string& file, const std::string& file2) {
|
||||
std::stringstream ss;
|
||||
ss << "(temporary file for " << file2 << ") Could not write to file";
|
||||
throw ad::cppgtfs::WriterException(ss.str(), file);
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
#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 << "(temporary file for " << targetPath << ") Could not open file";
|
||||
throw ad::cppgtfs::WriterException(ss.str(), sourcePath);
|
||||
}
|
||||
|
||||
// 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
|
||||
59
src/pfaedle/gtfs/Writer.h
Normal file
59
src/pfaedle/gtfs/Writer.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_GTFS_WRITER_H_
|
||||
#define PFAEDLE_GTFS_WRITER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#ifdef LIBZIP_FOUND
|
||||
#include <zip.h>
|
||||
#endif
|
||||
|
||||
#include "Feed.h"
|
||||
#include "ad/cppgtfs/Parser.h"
|
||||
#include "ad/cppgtfs/Writer.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace gtfs {
|
||||
|
||||
class Writer {
|
||||
public:
|
||||
Writer() {}
|
||||
|
||||
void write(Feed* sourceFeed, const std::string& path) const;
|
||||
|
||||
private:
|
||||
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;
|
||||
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 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
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_GTFS_WRITER_H_
|
||||
|
|
@ -7,12 +7,14 @@
|
|||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/gtfs/Feed.h"
|
||||
#include "util/String.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoEdgePL;
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
using pfaedle::gtfs::Trip;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace netgraph {
|
||||
|
|
@ -21,19 +23,32 @@ namespace netgraph {
|
|||
* A payload class for edges on a network graph - that is a graph
|
||||
* that exactly represents a physical public transit network
|
||||
*/
|
||||
class EdgePL : public GeoEdgePL<float> {
|
||||
class EdgePL {
|
||||
public:
|
||||
EdgePL() {}
|
||||
EdgePL(const util::geo::FLine& l, const std::set<const Trip*>& trips)
|
||||
: _l(l), _trips(trips) {}
|
||||
const util::geo::FLine* getGeom() const { return &_l; }
|
||||
void getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["numtrips"] = std::to_string(_trips.size());
|
||||
EdgePL(const LINE& l, const std::vector<const Trip*>& trips)
|
||||
: _l(l), _trips(trips) {
|
||||
for (const auto t : _trips) {
|
||||
_routeShortNames.insert(t->getRoute()->getShortName());
|
||||
_tripShortNames.insert(t->getShortname());
|
||||
}
|
||||
}
|
||||
const LINE* getGeom() const { return &_l; }
|
||||
util::json::Dict getAttrs() const {
|
||||
util::json::Dict obj;
|
||||
obj["num_trips"] = static_cast<int>(_trips.size());
|
||||
obj["route_short_names"] =
|
||||
util::json::Array(_routeShortNames.begin(), _routeShortNames.end());
|
||||
obj["trip_short_names"] =
|
||||
util::json::Array(_tripShortNames.begin(), _tripShortNames.end());
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
util::geo::FLine _l;
|
||||
std::set<const Trip*> _trips;
|
||||
LINE _l;
|
||||
std::vector<const Trip*> _trips;
|
||||
std::set<std::string> _routeShortNames;
|
||||
std::set<std::string> _tripShortNames;
|
||||
};
|
||||
} // namespace netgraph
|
||||
} // namespace pfaedle
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
using util::geo::Point;
|
||||
using util::geo::Line;
|
||||
using util::geo::FPoint;
|
||||
using util::geo::FLine;
|
||||
|
||||
|
||||
|
||||
namespace pfaedle {
|
||||
namespace netgraph {
|
||||
|
|
|
|||
|
|
@ -19,18 +19,16 @@ namespace netgraph {
|
|||
* A payload class for edges on a network graph - that is a graph
|
||||
* that exactly represents a physical public transit network
|
||||
*/
|
||||
class NodePL : public GeoNodePL<float> {
|
||||
class NodePL {
|
||||
public:
|
||||
NodePL() {}
|
||||
NodePL(const util::geo::FPoint& geom) { _geom = geom; } // NOLINT
|
||||
NodePL(const POINT& geom) { _geom = geom; } // NOLINT
|
||||
|
||||
const util::geo::FPoint* getGeom() const { return &_geom; }
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) const {
|
||||
UNUSED(attrs);
|
||||
}
|
||||
const POINT* getGeom() const { return &_geom; }
|
||||
util::json::Dict getAttrs() const { return util::json::Dict(); }
|
||||
|
||||
private:
|
||||
util::geo::FPoint _geom;
|
||||
POINT _geom;
|
||||
};
|
||||
} // namespace netgraph
|
||||
} // namespace pfaedle
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
using pfaedle::osm::BBoxIdx;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
BBoxIdx::BBoxIdx(float padding) : _padding(padding), _size(0) {}
|
||||
BBoxIdx::BBoxIdx(double padding) : _padding(padding), _size(0) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void BBoxIdx::add(Box<float> box) {
|
||||
void BBoxIdx::add(Box<double> box) {
|
||||
// division by 83.000m is only correct here around a latitude deg of 25,
|
||||
// but should be a good heuristic. 1 deg is around 63km at latitude deg of 44,
|
||||
// and 110 at deg=0, since we usually dont do map matching in the arctic,
|
||||
|
|
@ -24,21 +24,46 @@ void BBoxIdx::add(Box<float> box) {
|
|||
size_t BBoxIdx::size() const { return _size; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool BBoxIdx::contains(const Point<float>& p) const {
|
||||
bool BBoxIdx::contains(const Point<double>& p) const {
|
||||
return treeHas(p, _root);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
util::geo::Box<float> BBoxIdx::getFullWebMercBox() const {
|
||||
return util::geo::FBox(
|
||||
util::geo::latLngToWebMerc<float>(_root.box.min_corner().get<1>(),
|
||||
_root.box.min_corner().get<0>()),
|
||||
util::geo::latLngToWebMerc<float>(_root.box.max_corner().get<1>(),
|
||||
_root.box.max_corner().get<0>()));
|
||||
BOX BBoxIdx::getFullWebMercBox() const {
|
||||
return BOX(
|
||||
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()));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool BBoxIdx::treeHas(const Point<float>& p, const BBoxIdxNd& nd) const {
|
||||
BOX BBoxIdx::getFullBox() const { return _root.box; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::vector<util::geo::Box<double>> BBoxIdx::getLeafs() const {
|
||||
std::vector<util::geo::Box<double>> ret;
|
||||
getLeafsRec(_root, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void BBoxIdx::getLeafsRec(const BBoxIdxNd& nd,
|
||||
std::vector<util::geo::Box<double>>* ret) const {
|
||||
if (!nd.childs.size()) {
|
||||
ret->push_back(nd.box);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& child : nd.childs) {
|
||||
getLeafsRec(child, ret);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool BBoxIdx::treeHas(const Point<double>& p, const BBoxIdxNd& nd) const {
|
||||
if (!nd.childs.size()) return util::geo::contains(p, nd.box);
|
||||
for (const auto& child : nd.childs) {
|
||||
if (util::geo::contains(p, child.box)) return treeHas(p, child);
|
||||
|
|
@ -48,7 +73,7 @@ bool BBoxIdx::treeHas(const Point<float>& p, const BBoxIdxNd& nd) const {
|
|||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void BBoxIdx::addToTree(const Box<float>& box, BBoxIdxNd* nd, size_t lvl) {
|
||||
void BBoxIdx::addToTree(const Box<double>& box, BBoxIdxNd* nd, size_t lvl) {
|
||||
double bestCommonArea = 0;
|
||||
ssize_t bestChild = -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define PFAEDLE_OSM_BBOXIDX_H_
|
||||
|
||||
#include <vector>
|
||||
#include "pfaedle/Def.h"
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
namespace pfaedle {
|
||||
|
|
@ -15,9 +16,9 @@ using util::geo::Box;
|
|||
using util::geo::Point;
|
||||
|
||||
struct BBoxIdxNd {
|
||||
BBoxIdxNd() : box(util::geo::minbox<float>()) {}
|
||||
explicit BBoxIdxNd(const Box<float>& box) : box(box) {}
|
||||
Box<float> box;
|
||||
BBoxIdxNd() : box(util::geo::minbox<double>()) {}
|
||||
explicit BBoxIdxNd(const Box<double>& box) : box(box) {}
|
||||
Box<double> box;
|
||||
std::vector<BBoxIdxNd> childs;
|
||||
};
|
||||
|
||||
|
|
@ -26,28 +27,37 @@ struct BBoxIdxNd {
|
|||
*/
|
||||
class BBoxIdx {
|
||||
public:
|
||||
explicit BBoxIdx(float padding);
|
||||
explicit BBoxIdx(double padding);
|
||||
|
||||
// Add a bounding box to this index
|
||||
void add(Box<float> box);
|
||||
void add(Box<double> box);
|
||||
|
||||
// Check if a point is contained in this index
|
||||
bool contains(const Point<float>& box) const;
|
||||
bool contains(const Point<double>& box) const;
|
||||
|
||||
// Return the full total bounding box of this index
|
||||
util::geo::Box<float> getFullWebMercBox() const;
|
||||
BOX getFullWebMercBox() const;
|
||||
|
||||
// Return the full total bounding box of this index
|
||||
BOX getFullBox() const;
|
||||
|
||||
// Return the size of this index
|
||||
size_t size() const;
|
||||
|
||||
// return the leaf bounding boxes of this idx
|
||||
std::vector<Box<double>> getLeafs() const;
|
||||
|
||||
private:
|
||||
double _padding;
|
||||
size_t _size;
|
||||
|
||||
BBoxIdxNd _root;
|
||||
|
||||
void addToTree(const Box<float>& box, BBoxIdxNd* nd, size_t lvl);
|
||||
bool treeHas(const Point<float>& p, const BBoxIdxNd& nd) const;
|
||||
void addToTree(const Box<double>& box, BBoxIdxNd* nd, size_t lvl);
|
||||
bool treeHas(const Point<double>& p, const BBoxIdxNd& nd) const;
|
||||
|
||||
void getLeafsRec(const BBoxIdxNd& nd,
|
||||
std::vector<util::geo::Box<double>>* ret) const;
|
||||
|
||||
static const size_t MAX_LVL = 5;
|
||||
static constexpr double MIN_COM_AREA = 0.0;
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -4,15 +4,15 @@
|
|||
|
||||
#ifndef PFAEDLE_OSM_OSMBUILDER_H_
|
||||
#define PFAEDLE_OSM_OSMBUILDER_H_
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/Def.h"
|
||||
#include "pfaedle/osm/BBoxIdx.h"
|
||||
#include "pfaedle/osm/OsmFilter.h"
|
||||
#include "pfaedle/osm/OsmIdSet.h"
|
||||
|
|
@ -23,26 +23,26 @@
|
|||
#include "pfaedle/trgraph/Normalizer.h"
|
||||
#include "pfaedle/trgraph/StatInfo.h"
|
||||
#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 {
|
||||
|
|
@ -57,10 +57,9 @@ struct SearchFunc {
|
|||
};
|
||||
|
||||
struct EqSearch : public SearchFunc {
|
||||
EqSearch() {}
|
||||
double minSimi = 0.9;
|
||||
bool operator()(const Node* cand, const StatInfo* si) const {
|
||||
return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi;
|
||||
}
|
||||
bool operator()(const Node* cand, const StatInfo* si) const;
|
||||
};
|
||||
|
||||
struct BlockSearch : public SearchFunc {
|
||||
|
|
@ -86,9 +85,18 @@ 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
|
||||
|
|
@ -97,37 +105,37 @@ class OsmBuilder {
|
|||
const std::vector<OsmReadOpts>& opts, const BBoxIdx& box);
|
||||
|
||||
private:
|
||||
xml::ParserState readBBoxNds(xml::File* xml, OsmIdSet* nodes,
|
||||
OsmIdSet* noHupNodes, const OsmFilter& filter,
|
||||
const BBoxIdx& bbox) const;
|
||||
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,
|
||||
|
|
@ -135,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;
|
||||
|
|
@ -159,72 +168,67 @@ 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;
|
||||
|
||||
Nullable<StatInfo> getStatInfo(Node* node, osmid nid, const FPoint& pos,
|
||||
const AttrMap& m, StAttrGroups* groups,
|
||||
protected:
|
||||
Nullable<StatInfo> getStatInfo(osmid nid, const AttrMap& m,
|
||||
const RelMap& nodeRels, const RelLst& rels,
|
||||
const OsmReadOpts& ops) const;
|
||||
|
||||
void writeGeoms(Graph* g) const;
|
||||
void deleteOrphNds(Graph* g) const;
|
||||
void deleteOrphEdgs(Graph* g) const;
|
||||
double dist(const Node* a, const Node* b) const;
|
||||
double webMercDist(const Node* a, const Node* b) const;
|
||||
double webMercDistFactor(const FPoint& a) const;
|
||||
static void snapStats(const OsmReadOpts& opts, Graph* g, const BBoxIdx& bbox,
|
||||
double gridSize, Restrictor* res,
|
||||
const NodeSet& orphanStations);
|
||||
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);
|
||||
|
||||
NodeGrid buildNodeIdx(Graph* g, size_t size,
|
||||
const util::geo::Box<float>& webMercBox,
|
||||
bool which) const;
|
||||
static NodeGrid buildNodeIdx(Graph* g, double size, const BOX& box,
|
||||
bool which);
|
||||
|
||||
EdgeGrid buildEdgeIdx(Graph* g, size_t size,
|
||||
const util::geo::Box<float>& webMercBox) const;
|
||||
static EdgeGrid buildEdgeIdx(Graph* g, double size, const BOX& box);
|
||||
|
||||
void fixGaps(Graph* g, NodeGrid* ng) const;
|
||||
void collapseEdges(Graph* g) const;
|
||||
void writeODirEdgs(Graph* g, Restrictor* restor) const;
|
||||
void writeSelfEdgs(Graph* g) const;
|
||||
void writeEdgeTracks(const EdgTracks& tracks) const;
|
||||
void simplifyGeoms(Graph* g) const;
|
||||
uint32_t writeComps(Graph* g) const;
|
||||
bool edgesSim(const Edge* a, const Edge* b) const;
|
||||
const EdgePL& mergeEdgePL(Edge* a, Edge* b) const;
|
||||
void getEdgCands(const FPoint& s, EdgeCandPQ* ret, EdgeGrid* eg,
|
||||
double d) const;
|
||||
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, 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);
|
||||
|
||||
std::set<Node*> getMatchingNds(const NodePL& s, NodeGrid* ng, double d) const;
|
||||
|
||||
Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const;
|
||||
|
||||
NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
|
||||
const OsmReadOpts& opts, Restrictor* restor, bool surHeur,
|
||||
double maxD) const;
|
||||
static void snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
|
||||
const OsmReadOpts& opts, Restrictor* restor,
|
||||
double maxD);
|
||||
|
||||
// Checks if from the edge e, a station similar to si can be reach with less
|
||||
// than maxD distance and less or equal to "maxFullTurns" full turns. If
|
||||
// such a station exists, it is returned. If not, 0 is returned.
|
||||
Node* eqStatReach(const Edge* e, const StatInfo* si, const FPoint& p,
|
||||
double maxD, int maxFullTurns, double maxAng) const;
|
||||
static Node* eqStatReach(const Edge* e, const StatInfo* si, const POINT& p,
|
||||
double maxD, int maxFullTurns, double maxAng);
|
||||
|
||||
Node* depthSearch(const Edge* e, const StatInfo* si,
|
||||
const util::geo::FPoint& p, double maxD, int maxFullTurns,
|
||||
double minAngle, const SearchFunc& sfunc) const;
|
||||
static Node* depthSearch(const Edge* e, const StatInfo* si, const POINT& p,
|
||||
double maxD, int maxFullTurns, double minAngle,
|
||||
const SearchFunc& sfunc);
|
||||
|
||||
bool isBlocked(const Edge* e, const StatInfo* si, const FPoint& p,
|
||||
double maxD, int maxFullTurns, double minAngle) const;
|
||||
static bool isBlocked(const Edge* e, const StatInfo* si, const POINT& p,
|
||||
double maxD, int maxFullTurns, double minAngle);
|
||||
static bool keepFullTurn(const trgraph::Node* n, double ang);
|
||||
|
||||
StatGroup* groupStats(const NodeSet& s) const;
|
||||
static NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops);
|
||||
|
||||
std::vector<TransitEdgeLine*> getLines(const std::vector<size_t>& edgeRels,
|
||||
const RelLst& rels,
|
||||
const OsmReadOpts& ops);
|
||||
|
||||
NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops) const;
|
||||
|
||||
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;
|
||||
|
|
@ -233,6 +237,7 @@ class OsmBuilder {
|
|||
const AttrMap& attrs, const RelMap& entRels,
|
||||
const RelLst& rels,
|
||||
const Normalizer& norm) const;
|
||||
|
||||
std::vector<std::string> getAttrMatchRanked(const DeepAttrLst& rule, osmid id,
|
||||
const AttrMap& attrs,
|
||||
const RelMap& entRels,
|
||||
|
|
@ -244,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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -102,12 +108,12 @@ uint64_t OsmFilter::contained(const AttrMap& attrs, const Attr& attr) {
|
|||
// _____________________________________________________________________________
|
||||
uint8_t OsmFilter::level(const AttrMap& attrs) const {
|
||||
// the best matching level is always returned
|
||||
for (int16_t i = 0; i < 7; i++) {
|
||||
for (int16_t i = 0; i < 8; i++) {
|
||||
for (const auto& kv : attrs) {
|
||||
const auto& lkv = (_levels + i)->find(kv.first);
|
||||
if (lkv != (_levels + i)->end()) {
|
||||
for (const auto& val : lkv->second) {
|
||||
if (valMatches(kv.second, val.first)) return i + 1;
|
||||
if (valMatches(kv.second, val.first)) return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +175,7 @@ std::vector<std::string> OsmFilter::getAttrKeys() const {
|
|||
for (const auto& kv : _noRestr) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
for (const auto& kv : *(_levels + i)) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
|
|
@ -191,27 +197,6 @@ OsmFilter OsmFilter::merge(const OsmFilter& other) const {
|
|||
keep[kv.first].insert(kv.second.begin(), kv.second.end());
|
||||
}
|
||||
|
||||
// TODO(patrick): multi-level combination for filters. otherwise
|
||||
// filter drop filters meant as a refinement for keep filters
|
||||
// interfere with other keeps
|
||||
|
||||
// const auto* d = &_drop;
|
||||
|
||||
// for (size_t i = 0; i < 2; i++) {
|
||||
// for (const auto& kv : *d) {
|
||||
// if (keep.find(kv.first) != keep.end()) {
|
||||
// for (const auto& val : kv.second) {
|
||||
// if (keep[kv.first].find(val.first) == keep[kv.first].end()) {
|
||||
// drop[kv.first].insert(val);
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// drop[kv.first].insert(kv.second.begin(), kv.second.end());
|
||||
// }
|
||||
// }
|
||||
// d = &other._drop;
|
||||
// }
|
||||
|
||||
return OsmFilter(keep, drop);
|
||||
}
|
||||
|
||||
|
|
@ -258,3 +243,13 @@ uint64_t OsmFilter::posRestr(const AttrMap& attrs) const {
|
|||
if (contained(attrs, _noRestr, ALL)) return false;
|
||||
return (contained(attrs, _posRestr, ALL));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const pfaedle::osm::MultAttrMap& OsmFilter::getKeepRules() const {
|
||||
return _keep;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const pfaedle::osm::MultAttrMap& OsmFilter::getDropRules() const {
|
||||
return _drop;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,12 +30,16 @@ 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;
|
||||
|
||||
OsmFilter merge(const OsmFilter& other) const;
|
||||
|
||||
const MultAttrMap& getKeepRules() const;
|
||||
const MultAttrMap& getDropRules() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
static bool valMatches(const std::string& a, const std::string& b, bool m);
|
||||
|
|
@ -43,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -283,20 +317,8 @@ int OsmIdSet::openTmpFile() const {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef __unix__
|
||||
posix_fadvise(file, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
#include <vector>
|
||||
#include "pfaedle/osm/Osm.h"
|
||||
|
||||
#ifndef POSIX_FADV_SEQUENTIAL
|
||||
#define POSIX_FADV_SEQUENTIAL 2
|
||||
#endif
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
|
|
@ -21,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
|
||||
|
|
@ -35,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;
|
||||
|
||||
|
|
@ -43,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;
|
||||
|
|
@ -53,6 +60,8 @@ class OsmIdSet {
|
|||
osmid _smallest;
|
||||
osmid _biggest;
|
||||
|
||||
bool _hasInv;
|
||||
|
||||
size_t _obufpos;
|
||||
mutable size_t _curBlock;
|
||||
mutable ssize_t _curBlockSize;
|
||||
|
|
@ -60,13 +69,14 @@ class OsmIdSet {
|
|||
// bloom filter
|
||||
std::bitset<BLOOMF_BITS>* _bitset;
|
||||
|
||||
std::bitset<BLOOMF_BITS>* _bitsetNotIn;
|
||||
|
||||
mutable std::vector<osmid> _blockEnds;
|
||||
|
||||
mutable size_t _fsize;
|
||||
|
||||
uint32_t knuth(uint32_t in) const;
|
||||
uint32_t jenkins(uint32_t in) const;
|
||||
uint32_t hash(uint32_t in, int i) const;
|
||||
void diskAdd(osmid id);
|
||||
void close() const;
|
||||
void sort() const;
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
#ifndef PFAEDLE_OSM_OSMREADOPTS_H_
|
||||
#define PFAEDLE_OSM_OSMREADOPTS_H_
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include "pfaedle/osm/Osm.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "pfaedle/trgraph/Normalizer.h"
|
||||
|
|
@ -77,54 +77,42 @@ 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 {
|
||||
DeepAttrLst nameRule;
|
||||
DeepAttrLst platformRule;
|
||||
DeepAttrLst idRule;
|
||||
};
|
||||
|
||||
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() {}
|
||||
|
||||
MultAttrMap noHupFilter;
|
||||
MultAttrMap keepFilter;
|
||||
MultAttrMap levelFilters[7];
|
||||
MultAttrMap levelFilters[8];
|
||||
MultAttrMap dropFilter;
|
||||
MultAttrMap oneWayFilter;
|
||||
MultAttrMap oneWayFilterRev;
|
||||
MultAttrMap twoWayFilter;
|
||||
MultAttrMap stationFilter;
|
||||
MultAttrMap stationBlockerFilter;
|
||||
std::vector<StatGroupNAttrRule> statGroupNAttrRules;
|
||||
MultAttrMap turnCycleFilter;
|
||||
|
||||
trgraph::Normalizer statNormzer;
|
||||
trgraph::Normalizer lineNormzer;
|
||||
trgraph::Normalizer trackNormzer;
|
||||
trgraph::Normalizer idNormzer;
|
||||
|
||||
RelLineRules relLinerules;
|
||||
StationAttrRules statAttrRules;
|
||||
|
|
@ -134,15 +122,25 @@ struct OsmReadOpts {
|
|||
uint8_t maxSnapLevel;
|
||||
|
||||
double maxAngleSnapReach;
|
||||
std::vector<double> maxSnapDistances;
|
||||
double maxSnapFallbackHeurDistance;
|
||||
double maxGroupSearchDistance;
|
||||
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;
|
||||
|
||||
// restriction system
|
||||
MultAttrMap restrPosRestr;
|
||||
|
|
@ -151,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;
|
||||
}
|
||||
|
||||
|
|
@ -170,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.maxGroupSearchDistance - b.maxGroupSearchDistance) < 0.1 &&
|
||||
fabs(a.maxSnapDistance - b.maxSnapDistance) < 0.1 &&
|
||||
fabs(a.maxStationCandDistance - b.maxStationCandDistance) < 0.1 &&
|
||||
fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[2] - b.levelSnapPunishFac[2]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[3] - b.levelSnapPunishFac[3]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[0] - b.levelDefSpeed[0]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[1] - b.levelDefSpeed[1]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[2] - b.levelDefSpeed[2]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[3] - b.levelDefSpeed[3]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[4] - b.levelDefSpeed[4]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[5] - b.levelDefSpeed[5]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[6] - b.levelDefSpeed[6]) < 0.1 &&
|
||||
fabs(a.levelDefSpeed[7] - b.levelDefSpeed[7]) < 0.1 &&
|
||||
fabs(a.oneWaySpeedPen - b.oneWaySpeedPen) < 0.1 &&
|
||||
fabs(a.oneWayEntryCost - b.oneWayEntryCost) < 0.1 &&
|
||||
fabs(a.noLinesPunishFact - b.noLinesPunishFact) < 0.1 &&
|
||||
fabs(a.fullTurnAngle - b.fullTurnAngle) < 0.1 &&
|
||||
fabs(a.maxSpeedCorFac - b.maxSpeedCorFac) < 0.1 &&
|
||||
fabs(a.maxSpeed - b.maxSpeed) < 0.1 &&
|
||||
a.restrPosRestr == b.restrPosRestr &&
|
||||
a.restrNegRestr == b.restrNegRestr &&
|
||||
a.noRestrFilter == b.noRestrFilter;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace router {
|
|||
using util::editDist;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double statSimi(const std::string& a, const std::string& b) {
|
||||
inline bool statSimi(const std::string& a, const std::string& b) {
|
||||
if (a == b) return 1;
|
||||
|
||||
if (a.empty() || b.empty()) return 0;
|
||||
|
|
@ -55,16 +55,33 @@ 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;
|
||||
|
||||
// if one of the lines is completely contained in the other, return 1
|
||||
if (a.find(b) != std::string::npos) {
|
||||
return 1;
|
||||
} else if (b.find(a) != std::string::npos) {
|
||||
return 1;
|
||||
if (a.size() > b.size() + 1) {
|
||||
// check if a begins with b
|
||||
if (a.compare(0, b.size() + 1, b + " ") == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if a ends with b
|
||||
if (a.compare(a.size() - (b.size() + 1), b.size() + 1, " " + b) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (b.size() > a.size() + 1) {
|
||||
// check if b begins with a
|
||||
if (b.compare(0, a.size() + 1, a + " ") == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if b ends with a
|
||||
if (b.compare(b.size() - (a.size() + 1), a.size() + 1, " " + a) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#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 FPoint& EdgePL::frontHop() const {
|
||||
if (!_edges.size()) return *_end->pl().getGeom();
|
||||
return _edges.back()->pl().frontHop();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const FPoint& 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 util::geo::FLine* 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; }
|
||||
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["cost"] = std::to_string(_cost.getValue());
|
||||
(*obj)["from_edge"] = util::toString(_startE);
|
||||
(*obj)["to_edge"] = util::toString(_endE);
|
||||
(*obj)["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
|
||||
(*obj)["cost_m_lvl0"] = std::to_string(_cost.meterDist);
|
||||
(*obj)["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
|
||||
(*obj)["cost_m_lvl2"] = std::to_string(_cost.meterDistLvl2);
|
||||
(*obj)["cost_m_lvl3"] = std::to_string(_cost.meterDistLvl3);
|
||||
(*obj)["cost_m_lvl4"] = std::to_string(_cost.meterDistLvl4);
|
||||
(*obj)["cost_m_lvl5"] = std::to_string(_cost.meterDistLvl5);
|
||||
(*obj)["cost_m_lvl6"] = std::to_string(_cost.meterDistLvl6);
|
||||
(*obj)["cost_m_lvl7"] = std::to_string(_cost.meterDistLvl7);
|
||||
(*obj)["cost_fullturn"] = std::to_string(_cost.fullTurns);
|
||||
(*obj)["cost_st_passthru"] = std::to_string(_cost.passThruStations);
|
||||
(*obj)["cost_m_oneway"] = std::to_string(_cost.oneWayMeters);
|
||||
(*obj)["cost_m_lineunmatch"] = std::to_string(_cost.lineUnmatchedMeters);
|
||||
(*obj)["cost_reach_node_pen"] = std::to_string(_cost.reachPen);
|
||||
(*obj)["cost_oneway_event"] = std::to_string(_cost.oneWayEdges);
|
||||
(*obj)["dummy"] = _edges.size() ? "no" : "yes";
|
||||
}
|
||||
|
|
@ -1,49 +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/router/Misc.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoEdgePL;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
class EdgePL : public GeoEdgePL<float> {
|
||||
public:
|
||||
EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {}
|
||||
const util::geo::FLine* getGeom() const;
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) 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 FPoint& frontHop() const;
|
||||
const FPoint& 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 util::geo::FLine _geom;
|
||||
};
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_EDGEPL_H_
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_GRAPH_H_
|
||||
#define PFAEDLE_ROUTER_GRAPH_H_
|
||||
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "pfaedle/router/EdgePL.h"
|
||||
#include "pfaedle/router/NodePL.h"
|
||||
#include "util/graph/DirGraph.h"
|
||||
|
||||
using util::geo::Point;
|
||||
using util::geo::Line;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
typedef util::graph::Edge<router::NodePL, router::EdgePL> Edge;
|
||||
typedef util::graph::Node<router::NodePL, router::EdgePL> Node;
|
||||
typedef util::graph::DirGraph<router::NodePL, router::EdgePL> Graph;
|
||||
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_GRAPH_H_
|
||||
40
src/pfaedle/router/HopCache.cpp
Normal file
40
src/pfaedle/router/HopCache.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2020, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <utility>
|
||||
#include <set>
|
||||
#include "pfaedle/router/HopCache.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "util/Misc.h"
|
||||
|
||||
using pfaedle::router::HopCache;
|
||||
using pfaedle::trgraph::Edge;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HopCache::setMin(const Edge* a, const Edge* b, uint32_t val) {
|
||||
_cache.set(a, b, val);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HopCache::setEx(const Edge* a, const Edge* b, uint32_t val) {
|
||||
int64_t v = val;
|
||||
_cache.set(a, b, -(v + 1));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HopCache::setMin(const Edge* a, const std::set<Edge*>& b, uint32_t val) {
|
||||
for (auto eb : b) _cache.set(a, eb, val);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void HopCache::setMin(const std::set<Edge*>& a, const Edge* b, uint32_t val) {
|
||||
for (auto ea : a) _cache.set(ea, b, val);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::pair<uint32_t, bool> HopCache::get(const Edge* a, const Edge* b) const {
|
||||
int64_t v = _cache.get(a, b);
|
||||
if (v < 0) return {(-v) - 1, 1};
|
||||
return {v, 0};
|
||||
}
|
||||
39
src/pfaedle/router/HopCache.h
Normal file
39
src/pfaedle/router/HopCache.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2020, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_HOPCACHE_H_
|
||||
#define PFAEDLE_ROUTER_HOPCACHE_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "util/Misc.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
class HopCache {
|
||||
public:
|
||||
void setMin(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val);
|
||||
|
||||
void setMin(const trgraph::Edge* a, const std::set<trgraph::Edge*>& b,
|
||||
uint32_t val);
|
||||
|
||||
void setMin(const std::set<trgraph::Edge*>& a, const trgraph::Edge* b,
|
||||
uint32_t val);
|
||||
|
||||
void setEx(const trgraph::Edge* a, const trgraph::Edge* b, uint32_t val);
|
||||
|
||||
std::pair<uint32_t, bool> get(const trgraph::Edge* a,
|
||||
const trgraph::Edge* b) const;
|
||||
|
||||
private:
|
||||
util::SparseMatrix<const trgraph::Edge*, int64_t, 0> _cache;
|
||||
};
|
||||
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_HOPCACHE_H_
|
||||
|
|
@ -7,11 +7,14 @@
|
|||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "ad/cppgtfs/gtfs/Route.h"
|
||||
#include "pfaedle/gtfs/Feed.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "util/Nullable.h"
|
||||
|
||||
using ad::cppgtfs::gtfs::Route;
|
||||
using ad::cppgtfs::gtfs::Stop;
|
||||
|
|
@ -19,178 +22,161 @@ using ad::cppgtfs::gtfs::Stop;
|
|||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
struct NodeCand {
|
||||
trgraph::Node* nd;
|
||||
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) {}
|
||||
double fullTurnPunishFac;
|
||||
stationDistPenFactor(0),
|
||||
turnRestrCost(0),
|
||||
popReachEdge(true),
|
||||
noSelfHops(true) {}
|
||||
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;
|
||||
}
|
||||
|
||||
struct EdgeCost {
|
||||
EdgeCost()
|
||||
: meterDist(0),
|
||||
meterDistLvl1(0),
|
||||
meterDistLvl2(0),
|
||||
meterDistLvl3(0),
|
||||
meterDistLvl4(0),
|
||||
meterDistLvl5(0),
|
||||
meterDistLvl6(0),
|
||||
meterDistLvl7(0),
|
||||
fullTurns(0),
|
||||
passThruStations(0),
|
||||
oneWayMeters(0),
|
||||
oneWayEdges(0),
|
||||
lineUnmatchedMeters(0),
|
||||
reachPen(0),
|
||||
o(0) {}
|
||||
EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3,
|
||||
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)
|
||||
: meterDist(mDist),
|
||||
meterDistLvl1(mDistLvl1),
|
||||
meterDistLvl2(mDistLvl2),
|
||||
meterDistLvl3(mDistLvl3),
|
||||
meterDistLvl4(mDistLvl4),
|
||||
meterDistLvl5(mDistLvl5),
|
||||
meterDistLvl6(mDistLvl6),
|
||||
meterDistLvl7(mDistLvl7),
|
||||
fullTurns(fullTurns),
|
||||
passThruStations(passThru),
|
||||
oneWayMeters(oneWayMeters),
|
||||
oneWayEdges(oneWayEdges),
|
||||
lineUnmatchedMeters(lineUnmatchedMeters),
|
||||
reachPen(reachPen),
|
||||
o(o) {}
|
||||
double meterDist;
|
||||
double meterDistLvl1;
|
||||
double meterDistLvl2;
|
||||
double meterDistLvl3;
|
||||
double meterDistLvl4;
|
||||
double meterDistLvl5;
|
||||
double meterDistLvl6;
|
||||
double meterDistLvl7;
|
||||
uint32_t fullTurns;
|
||||
int32_t passThruStations;
|
||||
double oneWayMeters;
|
||||
size_t oneWayEdges;
|
||||
double lineUnmatchedMeters;
|
||||
double reachPen;
|
||||
const RoutingOpts* o;
|
||||
|
||||
double getValue() const {
|
||||
if (!o) return meterDist + reachPen;
|
||||
return meterDist * o->levelPunish[0] + meterDistLvl1 * o->levelPunish[1] +
|
||||
meterDistLvl2 * o->levelPunish[2] +
|
||||
meterDistLvl3 * o->levelPunish[3] +
|
||||
meterDistLvl4 * o->levelPunish[4] +
|
||||
meterDistLvl5 * o->levelPunish[5] +
|
||||
meterDistLvl6 * o->levelPunish[6] +
|
||||
meterDistLvl7 * o->levelPunish[7] +
|
||||
oneWayMeters * o->oneWayPunishFac +
|
||||
oneWayEdges * o->oneWayEdgePunish +
|
||||
lineUnmatchedMeters * o->lineUnmatchedPunishFact +
|
||||
fullTurns * o->fullTurnPunishFac +
|
||||
passThruStations * o->passThruStationsPunish + reachPen;
|
||||
}
|
||||
|
||||
double getTotalMeters() const {
|
||||
return meterDist + meterDistLvl1 + meterDistLvl2 + meterDistLvl3 +
|
||||
meterDistLvl4 + meterDistLvl5 + meterDistLvl6 + meterDistLvl7;
|
||||
}
|
||||
};
|
||||
|
||||
inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) {
|
||||
return EdgeCost(
|
||||
a.meterDist + b.meterDist, a.meterDistLvl1 + b.meterDistLvl1,
|
||||
a.meterDistLvl2 + b.meterDistLvl2, a.meterDistLvl3 + b.meterDistLvl3,
|
||||
a.meterDistLvl4 + b.meterDistLvl4, a.meterDistLvl5 + b.meterDistLvl5,
|
||||
a.meterDistLvl6 + b.meterDistLvl6, a.meterDistLvl7 + b.meterDistLvl7,
|
||||
a.fullTurns + b.fullTurns, a.passThruStations + b.passThruStations,
|
||||
a.oneWayMeters + b.oneWayMeters, a.oneWayEdges + b.oneWayEdges,
|
||||
a.lineUnmatchedMeters + b.lineUnmatchedMeters, a.reachPen + b.reachPen,
|
||||
a.o ? a.o : b.o);
|
||||
}
|
||||
|
||||
inline bool operator<=(const EdgeCost& a, const EdgeCost& b) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
inline int angSmaller(const FPoint& f, const FPoint& m, const FPoint& 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;
|
||||
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)
|
||||
if (b.count(mot)) ret.insert(mot);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed,
|
||||
const MOTs mots,
|
||||
const std::string& tid) {
|
||||
pfaedle::router::FeedStops ret;
|
||||
for (auto t : feed->getTrips()) {
|
||||
if (!tid.empty() && t.getId() != tid) continue;
|
||||
if (mots.count(t.getRoute()->getType())) {
|
||||
for (auto st : t.getStopTimes()) {
|
||||
// if the station has type STATION_ENTRANCE, use the parent
|
||||
// station for routing. Normally, this should not occur, as
|
||||
// this is not allowed in stop_times.txt
|
||||
if (st.getStop()->getLocationType() ==
|
||||
ad::cppgtfs::gtfs::flat::Stop::STATION_ENTRANCE &&
|
||||
st.getStop()->getParentStation()) {
|
||||
ret[st.getStop()->getParentStation()] = 0;
|
||||
} else {
|
||||
ret[st.getStop()] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string getMotStr(const MOTs& mots) {
|
||||
MOTs tmp = mots;
|
||||
bool first = false;
|
||||
std::string motStr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return motStr;
|
||||
}
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +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"
|
||||
|
||||
using util::geograph::GeoNodePL;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
class NodePL : public GeoNodePL<float> {
|
||||
public:
|
||||
NodePL() : _n(0) {}
|
||||
NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT
|
||||
|
||||
const util::geo::FPoint* getGeom() const {
|
||||
return !_n ? 0 : _n->pl().getGeom();
|
||||
}
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) const {
|
||||
if (_n) _n->pl().getAttrs(attrs);
|
||||
}
|
||||
|
||||
private:
|
||||
const pfaedle::trgraph::Node* _n;
|
||||
};
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_NODEPL_H_
|
||||
|
|
@ -1,615 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <omp.h>
|
||||
#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;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
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;
|
||||
|
||||
double transitLinePen = 0; // transitLineCmp(e->pl());
|
||||
|
||||
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,
|
||||
e->pl().getLength() * transitLinePen, 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 {
|
||||
double best = 1;
|
||||
for (const auto* l : e.getLines()) {
|
||||
double cur = _rAttrs.simi(l);
|
||||
|
||||
if (cur < 0.0001) return cur;
|
||||
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()->get<0>();
|
||||
y += to->pl().getGeom()->get<1>();
|
||||
c++;
|
||||
}
|
||||
|
||||
x /= c;
|
||||
y /= c;
|
||||
_center = FPoint(x, y);
|
||||
|
||||
for (auto to : tos) {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::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()->get<0>();
|
||||
y += to->getFrom()->pl().getGeom()->get<1>();
|
||||
c++;
|
||||
}
|
||||
|
||||
x /= c;
|
||||
y /= c;
|
||||
_center = FPoint(x, y);
|
||||
|
||||
for (auto to : tos) {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::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 {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::webMercMeterDist(*a->getTo()->pl().getGeom(), _center) *
|
||||
_rOpts.levelPunish[_lvl]));
|
||||
|
||||
UNUSED(b);
|
||||
|
||||
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 {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::webMercMeterDist(*a->pl().getGeom(), _center)));
|
||||
|
||||
UNUSED(b);
|
||||
|
||||
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(const trgraph::Graph& g, size_t numThreads)
|
||||
: _g(g), _cache(numThreads) {
|
||||
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 NodeCandGroup& a, const NodeCandGroup& b) const {
|
||||
for (auto n1 : a) {
|
||||
for (auto n2 : b) {
|
||||
if (n1.nd->pl().getComp() == n2.nd->pl().getComp()) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const {
|
||||
double pend = 0;
|
||||
for (size_t i = 0; i < a.size(); i++) {
|
||||
for (size_t j = 0; j < b.size(); j++) {
|
||||
double d = util::geo::webMercMeterDist(*a[i].nd->pl().getGeom(),
|
||||
*b[j].nd->pl().getGeom());
|
||||
if (d > pend) pend = d;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters";
|
||||
|
||||
const trgraph::StatGroup* tgGrpTo = 0;
|
||||
if (b.begin()->nd->pl().getSI())
|
||||
tgGrpTo = b.begin()->nd->pl().getSI()->getGroup();
|
||||
|
||||
CostFunc costF(rAttrs, rOpts, rest, tgGrpTo, pend * 50);
|
||||
|
||||
std::set<trgraph::Edge *> from, to;
|
||||
|
||||
// TODO(patrick): test if the two sets share a common connected component
|
||||
|
||||
for (auto n : a)
|
||||
from.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end());
|
||||
|
||||
for (auto n : b)
|
||||
to.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end());
|
||||
|
||||
LOG(VDEBUG) << "Doing pilot run between " << from.size() << "->" << to.size()
|
||||
<< " 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 = static_cast<double>(static_cast<double>(
|
||||
util::geo::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;
|
||||
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 NodeCandRoute& 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++) {
|
||||
for (const auto* e : route[0][i].nd->getAdjListOut()) {
|
||||
// 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].nd);
|
||||
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()->nd->pl().getSI())
|
||||
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
|
||||
|
||||
std::set<trgraph::Edge*> froms;
|
||||
for (const auto& fr : route[i]) {
|
||||
froms.insert(fr.nd->getAdjListOut().begin(),
|
||||
fr.nd->getAdjListOut().end());
|
||||
}
|
||||
|
||||
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]) {
|
||||
assert(to.nd->getAdjListOut().size());
|
||||
for (auto eTo : to.nd->getAdjListOut()) {
|
||||
tos.insert(eTo);
|
||||
if (!nextNodes.count(eTo)) nextNodes[eTo] = cgraph->addNd(to.nd);
|
||||
if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink);
|
||||
|
||||
auto* ce = cgraph->addEdg(cNodeFr, nextNodes[eTo]);
|
||||
edges[eTo] = ce;
|
||||
pens[eTo] = to.pen;
|
||||
|
||||
edgeLists[eTo] = ce->pl().getEdges();
|
||||
ce->pl().setStartNode(eFr->getFrom());
|
||||
// for debugging
|
||||
ce->pl().setStartEdge(eFr);
|
||||
|
||||
ce->pl().setEndNode(to.nd);
|
||||
// for debugging
|
||||
ce->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 (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;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
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 (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 (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 ((*_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 (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(); }
|
||||
|
|
@ -5,189 +5,97 @@
|
|||
#ifndef PFAEDLE_ROUTER_ROUTER_H_
|
||||
#define PFAEDLE_ROUTER_ROUTER_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#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/graph/Dijkstra.h"
|
||||
#include "util/Misc.h"
|
||||
#include "util/geo/Geo.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),
|
||||
_max(max),
|
||||
_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;
|
||||
double _max;
|
||||
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;
|
||||
FPoint _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;
|
||||
FPoint _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
|
||||
Router(const trgraph::Graph& g, size_t numThreads);
|
||||
~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,
|
||||
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:
|
||||
const trgraph::Graph& _g;
|
||||
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;
|
||||
|
||||
mutable std::vector<Cache*> _cache;
|
||||
HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const;
|
||||
void hopsFast(const EdgeCandGroup& from, const EdgeCandGroup& to,
|
||||
const LayerCostsDAG& initCosts, CostMatrix* rCosts,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
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 NodeCandGroup& a, const NodeCandGroup& b) const;
|
||||
uint32_t addNonOverflow(uint32_t a, uint32_t b) const;
|
||||
};
|
||||
|
||||
#include "pfaedle/router/Router.tpp"
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
|
|
|
|||
629
src/pfaedle/router/Router.tpp
Normal file
629
src/pfaedle/router/Router.tpp
Normal 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();
|
||||
}
|
||||
|
|
@ -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,37 +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};
|
||||
|
||||
double simi(const TransitEdgeLine* line) const {
|
||||
auto i = _simiCache.find(line);
|
||||
if (i != _simiCache.end()) return i->second;
|
||||
|
||||
double cur = 1;
|
||||
if (router::lineSimi(line->shortName, shortName) > 0.5) cur -= 0.33;
|
||||
LineSimilarity ret{false, false, false};
|
||||
|
||||
if (line->toStr.empty() || router::statSimi(line->toStr, toString) > 0.5)
|
||||
cur -= 0.33;
|
||||
if (shortName.empty() || router::lineSimi(line->shortName, shortName) > 0.5)
|
||||
ret.nameSimilar = true;
|
||||
|
||||
if (line->fromStr.empty() ||
|
||||
router::statSimi(line->fromStr, fromString) > 0.5)
|
||||
cur -= 0.33;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_simiCache[line] = cur;
|
||||
if (lineFrom.empty() || classifier->similar(line->fromStr, lineFrom))
|
||||
ret.fromSimilar = true;
|
||||
|
||||
return cur;
|
||||
_simiCache[line] = ret;
|
||||
|
||||
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) {
|
||||
|
|
@ -52,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
|
|
@ -5,40 +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 ad::cppgtfs::gtfs::Trip;
|
||||
using ad::cppgtfs::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
|
||||
|
|
@ -46,74 +52,121 @@ typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
|
|||
*/
|
||||
class ShapeBuilder {
|
||||
public:
|
||||
ShapeBuilder(Feed* feed, MOTs mots, const config::MotConfig& motCfg,
|
||||
eval::Collector* ecoll, 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);
|
||||
|
||||
util::geo::FLine shapeL(const router::NodeCandRoute& ncr,
|
||||
const router::RoutingAttrs& rAttrs);
|
||||
util::geo::FLine 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 osm::BBoxIdx getPaddedGtfsBox(const Feed* feed, double pad,
|
||||
const MOTs& mots,
|
||||
const std::string& tid);
|
||||
static void getGtfsBox(const pfaedle::gtfs::Feed* feed, const MOTs& mots,
|
||||
const std::string& tid, bool dropShapes,
|
||||
osm::BBoxIdx* box, double maxSpeed,
|
||||
std::vector<double>* hopDists, uint8_t verbosity);
|
||||
|
||||
private:
|
||||
Feed* _feed;
|
||||
pfaedle::gtfs::Feed* _feed;
|
||||
MOTs _mots;
|
||||
config::MotConfig _motCfg;
|
||||
eval::Collector* _ecoll;
|
||||
config::Config _cfg;
|
||||
trgraph::Graph _g;
|
||||
router::Router _crouter;
|
||||
trgraph::Graph* _g;
|
||||
router::FeedStops* _stops;
|
||||
|
||||
router::FeedStops _stops;
|
||||
EdgeCandGroup _emptyNCG;
|
||||
|
||||
NodeCandGroup _emptyNCG;
|
||||
|
||||
size_t _curShpCnt, _numThreads;
|
||||
size_t _curShpCnt;
|
||||
|
||||
std::mutex _shpMutex;
|
||||
|
||||
TripRAttrs _rAttrs;
|
||||
|
||||
osm::Restrictor _restr;
|
||||
osm::Restrictor* _restr;
|
||||
const pfaedle::statsimiclassifier::StatsimiClassifier* _classifier;
|
||||
GrpCache _grpCache;
|
||||
|
||||
void writeMotStops();
|
||||
void buildGraph();
|
||||
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, 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
|
||||
|
||||
|
|
|
|||
48
src/pfaedle/router/Stats.h
Normal file
48
src/pfaedle/router/Stats.h
Normal 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_
|
||||
67
src/pfaedle/router/TripTrie.h
Normal file
67
src/pfaedle/router/TripTrie.h
Normal 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_
|
||||
246
src/pfaedle/router/TripTrie.tpp
Normal file
246
src/pfaedle/router/TripTrie.tpp
Normal 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];
|
||||
}
|
||||
259
src/pfaedle/router/Weights.cpp
Normal file
259
src/pfaedle/router/Weights.cpp
Normal 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();
|
||||
}
|
||||
161
src/pfaedle/router/Weights.h
Normal file
161
src/pfaedle/router/Weights.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_WEIGHTS_H_
|
||||
#define PFAEDLE_ROUTER_WEIGHTS_H_
|
||||
|
||||
#include <set>
|
||||
#include "pfaedle/osm/Restrictor.h"
|
||||
#include "pfaedle/router/Misc.h"
|
||||
#include "pfaedle/router/RoutingAttrs.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "util/graph/EDijkstra.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
typedef util::graph::EDijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL,
|
||||
uint32_t>
|
||||
RCostFunc;
|
||||
typedef util::graph::EDijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL,
|
||||
uint32_t>
|
||||
RHeurFunc;
|
||||
|
||||
class ExpoTransWeight {
|
||||
public:
|
||||
struct CostFunc : public RCostFunc {
|
||||
CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& res, uint32_t max)
|
||||
: _rAttrs(rAttrs),
|
||||
_rOpts(rOpts),
|
||||
_res(res),
|
||||
_inf(max),
|
||||
_noLineSimiPen(false),
|
||||
_lastFrom(0) {
|
||||
if (_rAttrs.lineFrom.empty() && _rAttrs.lineTo.empty() &&
|
||||
_rAttrs.shortName.empty()) {
|
||||
_noLineSimiPen = true;
|
||||
}
|
||||
if (_rOpts.lineUnmatchedPunishFact == 1) {
|
||||
_noLineSimiPen = true;
|
||||
}
|
||||
}
|
||||
|
||||
const RoutingAttrs& _rAttrs;
|
||||
const RoutingOpts& _rOpts;
|
||||
const osm::Restrictor& _res;
|
||||
uint32_t _inf;
|
||||
bool _noLineSimiPen;
|
||||
mutable const trgraph::Edge* _lastFrom;
|
||||
mutable uint32_t _lastC = 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_
|
||||
104
src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp
Normal file
104
src/pfaedle/statsimi-classifier/StatsimiClassifier.cpp
Normal 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
|
||||
}
|
||||
68
src/pfaedle/statsimi-classifier/StatsimiClassifier.h
Normal file
68
src/pfaedle/statsimi-classifier/StatsimiClassifier.h
Normal 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_
|
||||
2
src/pfaedle/tests/CMakeLists.txt
Normal file
2
src/pfaedle/tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
add_executable(pfaedleTest TestMain.cpp)
|
||||
target_link_libraries(pfaedleTest pfaedle_dep util)
|
||||
329
src/pfaedle/tests/TestMain.cpp
Normal file
329
src/pfaedle/tests/TestMain.cpp
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
// Copyright 2020
|
||||
// Author: Patrick Brosi
|
||||
|
||||
#include "pfaedle/osm/Restrictor.h"
|
||||
|
||||
#define private public
|
||||
#include "pfaedle/router/Router.h"
|
||||
#undef private
|
||||
#define private private
|
||||
|
||||
using pfaedle::osm::Restrictor;
|
||||
using pfaedle::router::CostMatrix;
|
||||
using pfaedle::router::EdgeCandGroup;
|
||||
using pfaedle::router::ExpoTransWeight;
|
||||
using pfaedle::router::LayerCostsDAG;
|
||||
using pfaedle::router::RouterImpl;
|
||||
using pfaedle::router::RoutingAttrs;
|
||||
using pfaedle::router::RoutingOpts;
|
||||
using util::approx;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint32_t cmGet(const CostMatrix& m, size_t i, size_t j) {
|
||||
for (const auto& e : m) {
|
||||
if (e.first.first == i && e.first.second == j) return e.second;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
int main(int argc, char** argv) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
RouterImpl<ExpoTransWeight> router;
|
||||
|
||||
RoutingAttrs rAttrs;
|
||||
RoutingOpts rOpts;
|
||||
Restrictor restr;
|
||||
LayerCostsDAG initCosts;
|
||||
|
||||
// to make sure we always underestimate the cost in the heuristic for testing
|
||||
pfaedle::trgraph::NodePL::comps.emplace_back(
|
||||
pfaedle::trgraph::Component{9999999});
|
||||
|
||||
// build transit graph
|
||||
pfaedle::trgraph::Graph g;
|
||||
auto a = g.addNd(POINT{0, 0});
|
||||
auto b = g.addNd(POINT{0, 10});
|
||||
auto c = g.addNd(POINT{10, 0});
|
||||
auto d = g.addNd(POINT{20, 0});
|
||||
|
||||
a->pl().setComp(1);
|
||||
b->pl().setComp(1);
|
||||
c->pl().setComp(1);
|
||||
d->pl().setComp(1);
|
||||
|
||||
auto eA = g.addEdg(a, c);
|
||||
auto eB = g.addEdg(b, c);
|
||||
auto eC = g.addEdg(c, d);
|
||||
|
||||
eA->pl().setCost(10);
|
||||
eB->pl().setCost(6);
|
||||
eC->pl().setCost(100);
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0, {}, 0, {}});
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(10));
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(6));
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0.5, {}, 0, {}});
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(50 + 10));
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(50 + 6));
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0, {}, 0, {}});
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(5));
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(2));
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0.9, {}, 0, {}});
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hops(froms, tos, &costM, &dists, rAttrs, rOpts, restr, &c, maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(90 + 5));
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(90 + 2));
|
||||
}
|
||||
|
||||
// with hopsfast
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{0, 0};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), >=, maxTime);
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(6));
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0.5, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{0, 0};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), >=, maxTime);
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(50 + 6));
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{0, 0};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), >=, maxTime);
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(2));
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 2.0 / 3.0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0.9, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{0, 0};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), >=, maxTime);
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(90 + 2));
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{0, 0};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(5));
|
||||
TEST(cmGet(costM, 1, 0), >=, maxTime);
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{9999, 0};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(5));
|
||||
TEST(cmGet(costM, 1, 0), >=, maxTime);
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eA, 0, 0, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{6, 0, 20};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
// we also get this, because the edge is the same!
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(5));
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(10));
|
||||
TEST(cmGet(costM, 2, 0), >=, maxTime);
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eA, 0, 0, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 1, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{6, 0, 20};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
// we also get this, because the edge is the same!
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(5 + 100));
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(10 + 100));
|
||||
TEST(cmGet(costM, 2, 0), >=, maxTime);
|
||||
}
|
||||
|
||||
{
|
||||
EdgeCandGroup froms, tos;
|
||||
CostMatrix costM, dists;
|
||||
froms.push_back({eA, 0, 0.5, {}, 0, {}});
|
||||
froms.push_back({eA, 0, 0, {}, 0, {}});
|
||||
froms.push_back({eB, 0, 0, {}, 0, {}});
|
||||
|
||||
tos.push_back({eC, 0, 1, {}, 0, {}});
|
||||
tos.push_back({eC, 0, 0.5, {}, 0, {}});
|
||||
|
||||
LayerCostsDAG initCost{6, 0, 20};
|
||||
|
||||
double maxTime = 9999;
|
||||
|
||||
pfaedle::router::HopCache c;
|
||||
|
||||
router.hopsFast(froms, tos, initCost, &costM, rAttrs, rOpts, restr, &c,
|
||||
maxTime);
|
||||
|
||||
// we also get this, because the edge is the same!
|
||||
TEST(cmGet(costM, 0, 0), ==, approx(5 + 100));
|
||||
TEST(cmGet(costM, 1, 0), ==, approx(10 + 100));
|
||||
TEST(cmGet(costM, 0, 1), ==, approx(5 + 50));
|
||||
TEST(cmGet(costM, 1, 1), ==, approx(10 + 50));
|
||||
TEST(cmGet(costM, 2, 0), >=, maxTime);
|
||||
TEST(cmGet(costM, 2, 1), >=, maxTime);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -6,18 +6,17 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include "pfaedle/trgraph/EdgePL.h"
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
using pfaedle::trgraph::EdgePL;
|
||||
using pfaedle::trgraph::TransitEdgeLine;
|
||||
|
||||
std::map<util::geo::FLine*, size_t> EdgePL::_flines;
|
||||
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 util::geo::FLine();
|
||||
_flines[_l] = 1;
|
||||
: _oneWay(0), _hasRestr(false), _rev(false), _lvl(0), _cost(0), _l(0) {
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
|
@ -25,19 +24,22 @@ EdgePL::EdgePL(const EdgePL& pl) : EdgePL(pl, false) {}
|
|||
|
||||
// _____________________________________________________________________________
|
||||
EdgePL::EdgePL(const EdgePL& pl, bool geoflat)
|
||||
: _length(pl._length),
|
||||
_oneWay(pl._oneWay),
|
||||
: _oneWay(pl._oneWay),
|
||||
_hasRestr(pl._hasRestr),
|
||||
_rev(pl._rev),
|
||||
_lvl(pl._lvl) {
|
||||
if (geoflat) {
|
||||
_l = pl._l;
|
||||
} else {
|
||||
_l = new util::geo::FLine(*pl._l);
|
||||
_lvl(pl._lvl),
|
||||
_cost(pl._cost),
|
||||
_l(0) {
|
||||
if (pl._l) {
|
||||
if (geoflat) {
|
||||
_l = pl._l;
|
||||
} else {
|
||||
_l = new LINE(*pl._l);
|
||||
}
|
||||
_flines[_l]++;
|
||||
}
|
||||
_flines[_l]++;
|
||||
|
||||
for (auto l : _lines) addLine(l);
|
||||
for (auto l : pl._lines) addLine(l);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
|
@ -73,14 +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 (_lines.insert(l).second) {
|
||||
auto lb = std::lower_bound(_lines.begin(), _lines.end(), l);
|
||||
if (lb == _lines.end() || *lb != l) {
|
||||
_lines.reserve(_lines.size() + 1);
|
||||
lb = std::lower_bound(_lines.begin(), _lines.end(), l);
|
||||
_lines.insert(lb, l);
|
||||
if (_tlines.count(l))
|
||||
_tlines[l]++;
|
||||
else
|
||||
|
|
@ -94,25 +105,33 @@ void EdgePL::addLines(const std::vector<TransitEdgeLine*>& l) {
|
|||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<const TransitEdgeLine*>& EdgePL::getLines() const {
|
||||
const std::vector<const TransitEdgeLine*>& EdgePL::getLines() const {
|
||||
return _lines;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::addPoint(const util::geo::FPoint& p) { _l->push_back(p); }
|
||||
void EdgePL::addPoint(const POINT& p) {
|
||||
if (!_l) {
|
||||
_l = new LINE();
|
||||
_flines[_l] = 1;
|
||||
}
|
||||
_l->push_back(p);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FLine* EdgePL::getGeom() const { return _l; }
|
||||
const LINE* EdgePL::getGeom() const { return _l; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
util::geo::FLine* EdgePL::getGeom() { return _l; }
|
||||
LINE* EdgePL::getGeom() { return _l; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["m_length"] = std::to_string(_length);
|
||||
(*obj)["oneway"] = std::to_string(static_cast<int>(_oneWay));
|
||||
(*obj)["level"] = std::to_string(_lvl);
|
||||
(*obj)["restriction"] = isRestricted() ? "yes" : "no";
|
||||
util::json::Dict EdgePL::getAttrs() const {
|
||||
util::json::Dict obj;
|
||||
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";
|
||||
|
||||
std::stringstream ss;
|
||||
bool first = false;
|
||||
|
|
@ -127,7 +146,8 @@ void EdgePL::getAttrs(std::map<std::string, std::string>* obj) const {
|
|||
first = true;
|
||||
}
|
||||
|
||||
(*obj)["lines"] = ss.str();
|
||||
obj["lines"] = ss.str();
|
||||
return obj;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
|
|
@ -146,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; }
|
||||
|
|
@ -158,7 +178,7 @@ void EdgePL::setRev() { _rev = true; }
|
|||
bool EdgePL::isRev() const { return _rev; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FPoint& EdgePL::backHop() const {
|
||||
const POINT& EdgePL::backHop() const {
|
||||
if (isRev()) {
|
||||
return *(++(getGeom()->cbegin()));
|
||||
}
|
||||
|
|
@ -166,7 +186,7 @@ const util::geo::FPoint& EdgePL::backHop() const {
|
|||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FPoint& EdgePL::frontHop() const {
|
||||
const POINT& EdgePL::frontHop() const {
|
||||
if (isRev()) {
|
||||
return *(++(getGeom()->crbegin()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "pfaedle/Def.h"
|
||||
#include "pfaedle/router/Comp.h"
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoEdgePL;
|
||||
|
|
@ -17,7 +19,6 @@ using util::geograph::GeoEdgePL;
|
|||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
|
||||
/*
|
||||
* A line occuring on an edge
|
||||
*/
|
||||
|
|
@ -25,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 &&
|
||||
|
|
@ -42,7 +46,7 @@ inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) {
|
|||
/*
|
||||
* An edge payload class for the transit graph.
|
||||
*/
|
||||
class EdgePL : public GeoEdgePL<float> {
|
||||
class EdgePL {
|
||||
public:
|
||||
EdgePL();
|
||||
~EdgePL();
|
||||
|
|
@ -50,26 +54,32 @@ class EdgePL : public GeoEdgePL<float> {
|
|||
EdgePL(const EdgePL& pl, bool geoFlat);
|
||||
|
||||
// Return the geometry of this edge.
|
||||
const util::geo::FLine* getGeom() const;
|
||||
util::geo::FLine* getGeom();
|
||||
const LINE* getGeom() const;
|
||||
LINE* getGeom();
|
||||
|
||||
// Extends this edge payload's geometry by Point p
|
||||
void addPoint(const util::geo::FPoint& p);
|
||||
void addPoint(const POINT& p);
|
||||
|
||||
// Fill obj with k/v pairs describing the parameters of this payload.
|
||||
void getAttrs(std::map<std::string, std::string>* obj) const;
|
||||
util::json::Dict getAttrs() const;
|
||||
|
||||
// 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();
|
||||
|
||||
|
|
@ -82,12 +92,6 @@ class EdgePL : public GeoEdgePL<float> {
|
|||
// 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;
|
||||
|
||||
|
|
@ -98,33 +102,33 @@ class EdgePL : public GeoEdgePL<float> {
|
|||
void addLines(const std::vector<TransitEdgeLine*>& l);
|
||||
|
||||
// Return the TransitEdgeLines stored for this payload
|
||||
const std::set<const TransitEdgeLine*>& getLines() const;
|
||||
const std::vector<const TransitEdgeLine*>& getLines() const;
|
||||
|
||||
// Returns the last hop of the payload - this is the (n-2)th point in
|
||||
// the payload geometry of length n > 1
|
||||
const util::geo::FPoint& backHop() const;
|
||||
const POINT& backHop() const;
|
||||
|
||||
// Returns the first hop of the payload - this is the 2nd point in
|
||||
// the payload geometry of length n > 1
|
||||
const util::geo::FPoint& frontHop() const;
|
||||
const POINT& frontHop() const;
|
||||
|
||||
// Obtain an exact copy of this edge, but in reverse.
|
||||
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
|
||||
|
||||
util::geo::FLine* _l;
|
||||
LINE* _l;
|
||||
|
||||
std::set<const TransitEdgeLine*> _lines;
|
||||
std::vector<const TransitEdgeLine*> _lines;
|
||||
|
||||
static void unRefTLine(const TransitEdgeLine* l);
|
||||
|
||||
static std::map<util::geo::FLine*, size_t> _flines;
|
||||
static std::map<LINE*, size_t> _flines;
|
||||
static std::map<const TransitEdgeLine*, size_t> _tlines;
|
||||
};
|
||||
} // namespace trgraph
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@
|
|||
using util::geo::Grid;
|
||||
using util::geo::Point;
|
||||
using util::geo::Line;
|
||||
using util::geo::FPoint;
|
||||
using util::geo::FLine;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
|
@ -26,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, float> NodeGrid;
|
||||
typedef Grid<Edge*, Line, float> EdgeGrid;
|
||||
typedef Grid<Node*, Point, PFDL_PREC> NodeGrid;
|
||||
typedef Grid<Edge*, Line, PFDL_PREC> EdgeGrid;
|
||||
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
|
|
|||
|
|
@ -3,122 +3,140 @@
|
|||
// 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() : _geom(0, 0), _si(0), _component(0), _vis(0) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NodePL::NodePL(const NodePL& pl)
|
||||
: _geom(pl._geom), _si(0), _component(pl._component), _vis(pl._vis) {
|
||||
if (pl._si) setSI(*(pl._si));
|
||||
NodePL::NodePL()
|
||||
: _geom(0, 0),
|
||||
_si(0),
|
||||
_component(0)
|
||||
#ifdef PFAEDLE_DBG
|
||||
,
|
||||
_vis(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NodePL::NodePL(const util::geo::FPoint& geom)
|
||||
: _geom(geom), _si(0), _component(0), _vis(0) {}
|
||||
NodePL::NodePL(const POINT& geom)
|
||||
: _geom(geom),
|
||||
_si(0),
|
||||
_component(0)
|
||||
#ifdef PFAEDLE_DBG
|
||||
,
|
||||
_vis(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NodePL::NodePL(const util::geo::FPoint& geom, const StatInfo& si)
|
||||
: _geom(geom), _si(0), _component(0), _vis(0) {
|
||||
NodePL::NodePL(const POINT& geom, const StatInfo& si)
|
||||
: _geom(geom),
|
||||
_si(0),
|
||||
_component(0)
|
||||
#ifdef PFAEDLE_DBG
|
||||
,
|
||||
_vis(0)
|
||||
#endif
|
||||
{
|
||||
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
|
||||
_vis = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setVisited() const { _vis = true; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
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;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FPoint* NodePL::getGeom() const { return &_geom; }
|
||||
const POINT* NodePL::getGeom() const { return &_geom; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setGeom(const util::geo::FPoint& geom) { _geom = geom; }
|
||||
void NodePL::setGeom(const POINT& geom) { _geom = geom; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["component"] = std::to_string(reinterpret_cast<size_t>(_component));
|
||||
(*obj)["dijkstra_vis"] = _vis ? "yes" : "no";
|
||||
util::json::Dict NodePL::getAttrs() const {
|
||||
util::json::Dict obj;
|
||||
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_info_ptr"] = util::toString(_si);
|
||||
obj["station_name"] = getSI()->getName();
|
||||
obj["station_alt_names"] =
|
||||
util::implode(getSI()->getAltNames(), ",");
|
||||
obj["station_platform"] = getSI()->getTrack();
|
||||
|
||||
std::stringstream gtfsIds;
|
||||
if (_si->getGroup()) {
|
||||
for (auto* s : _si->getGroup()->getStops()) {
|
||||
gtfsIds << s->getId() << " (" << s->getName() << "),";
|
||||
}
|
||||
}
|
||||
|
||||
(*obj)["station_group_stops"] = gtfsIds.str();
|
||||
#ifdef PFAEDLE_STATION_IDS
|
||||
// only print this in debug mode
|
||||
obj["station_id"] = getSI()->getId();
|
||||
#endif
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setSI(const StatInfo& si) { _si = new StatInfo(si); }
|
||||
void NodePL::setSI(const StatInfo& si) {
|
||||
_statInfos.emplace_back(si);
|
||||
_si = _statInfos.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const StatInfo* NodePL::getSI() const {
|
||||
if (isBlocker()) return 0;
|
||||
return _si;
|
||||
if (isTurnCycle()) return 0;
|
||||
if (_si == 0) return 0;
|
||||
return &_statInfos[_si - 1];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
StatInfo* NodePL::getSI() {
|
||||
if (isBlocker()) return 0;
|
||||
return _si;
|
||||
if (isTurnCycle()) return 0;
|
||||
if (_si == 0) return 0;
|
||||
return &_statInfos[_si - 1];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setBlocker() { _si = &_blockerSI; }
|
||||
void NodePL::setTurnCycle() { _si = std::numeric_limits<uint32_t>::max() - 1; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool NodePL::isBlocker() const { return _si == &_blockerSI; }
|
||||
bool NodePL::isTurnCycle() const {
|
||||
return _si == (std::numeric_limits<uint32_t>::max() - 1);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setBlocker() { _si = std::numeric_limits<uint32_t>::max(); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool NodePL::isBlocker() const {
|
||||
return _si == std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/Def.h"
|
||||
#include "pfaedle/trgraph/StatInfo.h"
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoNodePL;
|
||||
|
|
@ -18,26 +21,24 @@ namespace pfaedle {
|
|||
namespace trgraph {
|
||||
|
||||
struct Component {
|
||||
uint8_t minEdgeLvl : 3;
|
||||
float maxSpeed;
|
||||
};
|
||||
|
||||
/*
|
||||
* A node payload class for the transit graph.
|
||||
*/
|
||||
class NodePL : public GeoNodePL<float> {
|
||||
class NodePL {
|
||||
public:
|
||||
NodePL();
|
||||
NodePL(const NodePL& pl); // NOLINT
|
||||
NodePL(const util::geo::FPoint& geom); // NOLINT
|
||||
NodePL(const util::geo::FPoint& geom, const StatInfo& si);
|
||||
~NodePL();
|
||||
NodePL(const POINT& geom); // NOLINT
|
||||
NodePL(const POINT& geom, const StatInfo& si);
|
||||
|
||||
// Return the geometry of this node.
|
||||
const util::geo::FPoint* getGeom() const;
|
||||
void setGeom(const util::geo::FPoint& geom);
|
||||
const POINT* getGeom() const;
|
||||
void setGeom(const POINT& geom);
|
||||
|
||||
// Fill obj with k/v pairs describing the parameters of this payload.
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) const;
|
||||
util::json::Dict getAttrs() const;
|
||||
|
||||
// Set the station info for this node
|
||||
void setSI(const StatInfo& si);
|
||||
|
|
@ -50,10 +51,13 @@ class NodePL : public GeoNodePL<float> {
|
|||
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();
|
||||
|
|
@ -61,21 +65,27 @@ class NodePL : public GeoNodePL<float> {
|
|||
// 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:
|
||||
std::string _b;
|
||||
// 32bit floats are enough here
|
||||
util::geo::FPoint _geom;
|
||||
StatInfo* _si;
|
||||
const Component* _component;
|
||||
|
||||
static StatInfo _blockerSI;
|
||||
POINT _geom;
|
||||
uint32_t _si;
|
||||
uint32_t _component;
|
||||
|
||||
#ifdef PFAEDLE_DBG
|
||||
mutable bool _vis;
|
||||
|
||||
static std::unordered_map<const Component*, size_t> _comps;
|
||||
#endif
|
||||
static std::vector<StatInfo> _statInfos;
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
|
|
|||
|
|
@ -3,22 +3,41 @@
|
|||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "pfaedle/trgraph/Normalizer.h"
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -1,95 +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 {
|
||||
FPoint p = util::geo::latLngToWebMerc<float>(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)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_TRGRAPH_STATGROUP_H_
|
||||
#define PFAEDLE_TRGRAPH_STATGROUP_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/router/Router.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "pfaedle/trgraph/Normalizer.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
using ad::cppgtfs::gtfs::Stop;
|
||||
|
||||
/*
|
||||
* A group of stations that belong together semantically (for example, multiple
|
||||
* stop points of a larger bus station)
|
||||
*/
|
||||
class StatGroup {
|
||||
public:
|
||||
StatGroup();
|
||||
StatGroup(const StatGroup& a) = delete;
|
||||
|
||||
// Add a stop s to this station group
|
||||
void addStop(const Stop* s);
|
||||
|
||||
// Add a node n to this station group
|
||||
void addNode(trgraph::Node* n);
|
||||
|
||||
// Return all nodes contained in this group
|
||||
const std::set<trgraph::Node*>& getNodes() const;
|
||||
std::set<trgraph::Node*>& getNodes();
|
||||
|
||||
// Return all stops contained in this group
|
||||
const std::set<const Stop*>& getStops() const;
|
||||
|
||||
// Remove a node from this group
|
||||
void remNode(trgraph::Node* n);
|
||||
|
||||
// All nodes in other will be in this group, their SI's updated, and the
|
||||
// "other" group deleted.
|
||||
void merge(StatGroup* other);
|
||||
|
||||
// Return node candidates for stop s from this group
|
||||
const router::NodeCandGroup& getNodeCands(const Stop* s) const;
|
||||
|
||||
// Write the penalties for all stops contained in this group so far.
|
||||
void writePens(const trgraph::Normalizer& platformNorm, double trackPen,
|
||||
double distPenFac, double nonOsmPen);
|
||||
|
||||
private:
|
||||
std::set<trgraph::Node*> _nodes;
|
||||
std::set<const Stop*> _stops;
|
||||
|
||||
// for each stop in this group, a penalty for each of the nodes here, based on
|
||||
// its distance and optionally the track number
|
||||
std::unordered_map<const Stop*, router::NodeCandGroup> _stopNodePens;
|
||||
|
||||
double getPen(const Stop* s, trgraph::Node* n,
|
||||
const trgraph::Normalizer& norm, double trackPen,
|
||||
double distPenFac, double nonOsmPen) const;
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_TRGRAPH_STATGROUP_H_
|
||||
|
|
@ -3,63 +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; }
|
||||
|
|
@ -67,12 +28,6 @@ const std::string& StatInfo::getName() const { return _name; }
|
|||
// _____________________________________________________________________________
|
||||
const std::string& StatInfo::getTrack() const { return _track; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool StatInfo::isFromOsm() const { return _fromOsm; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatInfo::setIsFromOsm(bool is) { _fromOsm = is; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double StatInfo::simi(const StatInfo* other) const {
|
||||
if (!other) return 0;
|
||||
|
|
|
|||
|
|
@ -6,24 +6,20 @@
|
|||
#define PFAEDLE_TRGRAPH_STATINFO_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
// forward declaration
|
||||
class StatGroup;
|
||||
|
||||
/*
|
||||
* Meta information (name, alternative names, track, group...) of a single stop
|
||||
* Meta information (name, alternative names, track, ...) of a single stop
|
||||
*/
|
||||
class StatInfo {
|
||||
public:
|
||||
StatInfo();
|
||||
StatInfo(const StatInfo& si);
|
||||
StatInfo(const std::string& name, const std::string& track, bool _fromOsm);
|
||||
~StatInfo();
|
||||
StatInfo(const std::string& name, const std::string& track);
|
||||
|
||||
// Return this stops names.
|
||||
const std::string& getName() const;
|
||||
|
|
@ -43,27 +39,21 @@ 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; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
std::vector<std::string> _altNames;
|
||||
std::string _track;
|
||||
bool _fromOsm;
|
||||
StatGroup* _group;
|
||||
|
||||
static std::unordered_map<const StatGroup*, size_t> _groups;
|
||||
static void unRefGroup(StatGroup* g);
|
||||
#ifdef PFAEDLE_STATION_IDS
|
||||
// debug feature to store station ids from both OSM
|
||||
// and GTFS
|
||||
std::string _id;
|
||||
#endif
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
|
|
|||
17
src/shapevl/CMakeLists.txt
Normal file
17
src/shapevl/CMakeLists.txt
Normal 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
485
src/shapevl/Collector.cpp
Normal 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
129
src/shapevl/Collector.h
Normal 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_
|
||||
276
src/shapevl/ShapevlMain.cpp
Normal file
276
src/shapevl/ShapevlMain.cpp
Normal 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
1
src/util
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d1c30e9ec4cb68803be073d35beb6af2b860bda4
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
file(GLOB_RECURSE util_SRC *.cpp)
|
||||
list(REMOVE_ITEM util_SRC TestMain.cpp)
|
||||
add_library(util ${util_SRC})
|
||||
|
||||
add_subdirectory(tests)
|
||||
|
|
@ -1,78 +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 <chrono>
|
||||
|
||||
#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)
|
||||
|
||||
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 float 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
|
||||
float 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++;
|
||||
float f = 0;
|
||||
uint8_t n = 0;
|
||||
|
||||
for (; n < mn && *p >= '0' && *p <= '9'; n++, p++) {
|
||||
f = f * 10.0 + (*p - '0');
|
||||
}
|
||||
|
||||
if (n < 11)
|
||||
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_
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#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) {
|
||||
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_
|
||||
|
|
@ -1,158 +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];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline std::string implode(const std::vector<T>& vec, const char* del) {
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < vec.size(); i++) {
|
||||
if (i != 0) ss << del;
|
||||
ss << vec[i];
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_STRING_H_
|
||||
|
|
@ -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/PolyLine.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GEO_BEZIERCURVE_H_
|
||||
|
|
@ -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.template get<0>();
|
||||
_xp.b = 3.0 * (b.template get<0>() - a.template get<0>());
|
||||
_xp.c = 3.0 * (c.template get<0>() - b.template get<0>()) - _xp.b;
|
||||
_xp.d = d.template get<0>() - a.template get<0>() - _xp.c - _xp.b;
|
||||
|
||||
_yp.a = a.template get<1>();
|
||||
_yp.b = 3.0 * (b.template get<1>() - a.template get<1>());
|
||||
_yp.c = 3.0 * (c.template get<1>() - b.template get<1>()) - _yp.b;
|
||||
_yp.d = d.template get<1>() - a.template get<1>() - _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;
|
||||
}
|
||||
|
|
@ -1,773 +0,0 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
#ifndef UTIL_GEO_GEO_H_
|
||||
#define UTIL_GEO_GEO_H_
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <math.h>
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
|
||||
#include "util/Misc.h"
|
||||
|
||||
// -------------------
|
||||
// Geometry stuff
|
||||
// ------------------
|
||||
|
||||
namespace bgeo = boost::geometry;
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
|
||||
template <typename T>
|
||||
using Point = bgeo::model::point<T, 2, bgeo::cs::cartesian>;
|
||||
|
||||
template <typename T>
|
||||
using Line = bgeo::model::linestring<Point<T>>;
|
||||
|
||||
template <typename T>
|
||||
using MultiLine = bgeo::model::multi_linestring<Line<T>>;
|
||||
|
||||
template <typename T>
|
||||
using Polygon = bgeo::model::polygon<Point<T>>;
|
||||
|
||||
template <typename T>
|
||||
using MultiPolygon = bgeo::model::multi_polygon<Polygon<T>>;
|
||||
|
||||
template <typename T>
|
||||
using Box = bgeo::model::box<Point<T>>;
|
||||
|
||||
// convenience aliases
|
||||
|
||||
typedef Point<double> DPoint;
|
||||
typedef Point<float> FPoint;
|
||||
typedef Point<int> IPoint;
|
||||
|
||||
typedef Line<double> DLine;
|
||||
typedef Line<float> FLine;
|
||||
typedef Line<int> ILine;
|
||||
|
||||
typedef Box<double> DBox;
|
||||
typedef Box<float> FBox;
|
||||
typedef Box<int> IBox;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Line<T> rotate(const Line<T>& geo, double deg, const Point<T>& center) {
|
||||
Line<T> ret;
|
||||
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translate(
|
||||
-center.template get<0>(), -center.template get<1>());
|
||||
bgeo::strategy::transform::rotate_transformer<bgeo::degree, T, 2, 2> rotate(
|
||||
deg);
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translateBack(
|
||||
center.template get<0>(), center.template get<1>());
|
||||
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> translateRotate(
|
||||
prod(rotate.matrix(), translate.matrix()));
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> all(
|
||||
prod(translateBack.matrix(), translateRotate.matrix()));
|
||||
|
||||
bgeo::transform(geo, ret, all);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline MultiLine<T> rotate(const MultiLine<T>& geo, double deg,
|
||||
const Point<T>& center) {
|
||||
MultiLine<T> ret;
|
||||
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translate(
|
||||
-center.template get<0>(), -center.template get<1>());
|
||||
bgeo::strategy::transform::rotate_transformer<bgeo::degree, T, 2, 2> rotate(
|
||||
deg);
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translateBack(
|
||||
center.template get<0>(), center.template get<1>());
|
||||
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> translateRotate(
|
||||
prod(rotate.matrix(), translate.matrix()));
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> all(
|
||||
prod(translateBack.matrix(), translateRotate.matrix()));
|
||||
|
||||
bgeo::transform(geo, ret, all);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> pad(const Box<T>& box, double padding) {
|
||||
return Box<T>(Point<T>(box.min_corner().template get<0>() - padding,
|
||||
box.min_corner().template get<1>() - padding),
|
||||
Point<T>(box.max_corner().template get<0>() + padding,
|
||||
box.max_corner().template get<1>() + padding));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Line<T> rotate(const Line<T>& geo, double deg) {
|
||||
Point<T> center;
|
||||
bgeo::centroid(geo, center);
|
||||
return rotate(geo, deg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline MultiLine<T> rotate(const MultiLine<T>& geo, double deg) {
|
||||
Point<T> center;
|
||||
bgeo::centroid(geo, center);
|
||||
return rotate(geo, deg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> typename Geometry, typename T>
|
||||
inline Geometry<T> move(const Geometry<T>& geo, T x, T y) {
|
||||
Geometry<T> ret;
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translate(x, y);
|
||||
bgeo::transform(geo, ret, translate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: outfactor
|
||||
|
||||
template <typename T>
|
||||
struct RotatedBox {
|
||||
RotatedBox(const Box<T>& b, double rot, const Point<T>& center)
|
||||
: b(b), rotateDeg(rot), center(center) {}
|
||||
RotatedBox(const Box<T>& b, double rot) : b(b), rotateDeg(rot) {
|
||||
bgeo::centroid(b, center);
|
||||
}
|
||||
|
||||
Box<T> b;
|
||||
double rotateDeg;
|
||||
Point<T> center;
|
||||
|
||||
Polygon<T> getPolygon() {
|
||||
Polygon<T> hull;
|
||||
bgeo::convex_hull(b, hull);
|
||||
return rotate(hull, rotateDeg, center);
|
||||
}
|
||||
};
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> minbox() {
|
||||
return bgeo::make_inverse<Box<T>>();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline RotatedBox<T> shrink(const RotatedBox<T>& b, double d) {
|
||||
double xd =
|
||||
b.b.max_corner().template get<0>() - b.b.min_corner().template get<0>();
|
||||
double yd =
|
||||
b.b.max_corner().template get<1>() - b.b.min_corner().template get<1>();
|
||||
|
||||
if (xd <= 2 * d) d = xd / 2 - 1;
|
||||
if (yd <= 2 * d) d = yd / 2 - 1;
|
||||
|
||||
Box<T> r(Point<T>(b.b.min_corner().template get<0>() + d,
|
||||
b.b.min_corner().template get<1>() + d),
|
||||
Point<T>(b.b.max_corner().template get<0>() - d,
|
||||
b.b.max_corner().template get<1>() - d));
|
||||
|
||||
return RotatedBox<T>(r, b.rotateDeg, b.center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline bool doubleEq(double a, double b) { return fabs(a - b) < 0.000001; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry, typename Box>
|
||||
inline bool contains(const Geometry& geom, const Box& box) {
|
||||
return bgeo::within(geom, box);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool contains(const Point<T>& p1, const Point<T>& q1, const Point<T>& p2,
|
||||
const Point<T>& q2) {
|
||||
Line<T> a, b;
|
||||
a.push_back(p1);
|
||||
a.push_back(q1);
|
||||
b.push_back(p2);
|
||||
b.push_back(q2);
|
||||
|
||||
return bgeo::covered_by(a, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool contains(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T q2y) {
|
||||
Point<T> p1(p1x, p1y);
|
||||
Point<T> q1(q1x, q1y);
|
||||
Point<T> p2(p2x, p2y);
|
||||
Point<T> q2(q2x, q2y);
|
||||
|
||||
return contains(p1, q1, p2, q2);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool intersects(const Point<T>& p1, const Point<T>& q1,
|
||||
const Point<T>& p2, const Point<T>& q2) {
|
||||
/*
|
||||
* checks whether two line segments intersect
|
||||
*/
|
||||
Line<T> a, b;
|
||||
a.push_back(p1);
|
||||
a.push_back(q1);
|
||||
b.push_back(p2);
|
||||
b.push_back(q2);
|
||||
|
||||
return !(contains(p1, q1, p2, q2) || contains(p2, q2, p1, q1)) &&
|
||||
bgeo::intersects(a, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool intersects(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T q2y) {
|
||||
/*
|
||||
* checks whether two line segments intersect
|
||||
*/
|
||||
Point<T> p1(p1x, p1y);
|
||||
Point<T> q1(q1x, q1y);
|
||||
Point<T> p2(p2x, p2y);
|
||||
Point<T> q2(q2x, q2y);
|
||||
|
||||
return intersects(p1, q1, p2, q2);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> intersection(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x,
|
||||
T q2y) {
|
||||
/*
|
||||
* calculates the intersection between two line segments
|
||||
*/
|
||||
if (doubleEq(p1x, q1x) && doubleEq(p1y, q1y))
|
||||
return Point<T>(p1x, p1y); // TODO: <-- intersecting with a point??
|
||||
if (doubleEq(p2x, q1x) && doubleEq(p2y, q1y)) return Point<T>(p2x, p2y);
|
||||
if (doubleEq(p2x, q2x) && doubleEq(p2y, q2y))
|
||||
return Point<T>(p2x, p2y); // TODO: <-- intersecting with a point??
|
||||
if (doubleEq(p1x, q2x) && doubleEq(p1y, q2y)) return Point<T>(p1x, p1y);
|
||||
|
||||
double a = ((q2y - p2y) * (q1x - p1x)) - ((q2x - p2x) * (q1y - p1y));
|
||||
double u = (((q2x - p2x) * (p1y - p2y)) - ((q2y - p2y) * (p1x - p2x))) / a;
|
||||
|
||||
return Point<T>(p1x + (q1x - p1x) * u, p1y + (q1y - p1y) * u);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> intersection(const Point<T>& p1, const Point<T>& q1,
|
||||
const Point<T>& p2, const Point<T>& q2) {
|
||||
/*
|
||||
* calculates the intersection between two line segments
|
||||
*/
|
||||
return intersection(p1.template get<0>(), p1.template get<1>(),
|
||||
q1.template get<0>(), q1.template get<1>(),
|
||||
p2.template get<0>(), p2.template get<1>(),
|
||||
q2.template get<0>(), q2.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool lineIntersects(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x,
|
||||
T q2y) {
|
||||
/*
|
||||
* checks whether two lines intersect
|
||||
*/
|
||||
double EPSILON = 0.0000001;
|
||||
double a = ((q2y - p2y) * (q1x - p1x)) - ((q2x - p2x) * (q1y - p1y));
|
||||
|
||||
return a > EPSILON || a < -EPSILON;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool lineIntersects(const Point<T>& p1, const Point<T>& q1,
|
||||
const Point<T>& p2, const Point<T>& q2) {
|
||||
/*
|
||||
* checks whether two lines intersect
|
||||
*/
|
||||
return lineIntersects(p1.template get<0>(), p1.template get<1>(),
|
||||
q1.template get<0>(), q1.template get<1>(),
|
||||
p2.template get<0>(), p2.template get<1>(),
|
||||
q2.template get<0>(), q2.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double angBetween(double p1x, double p1y, double q1x, double q1y) {
|
||||
double dY = q1y - p1y;
|
||||
double dX = q1x - p1x;
|
||||
return atan2(dY, dX);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double angBetween(const Point<T>& p1, const Point<T>& q1) {
|
||||
return angBetween(p1.template get<0>(), p1.template get<1>(),
|
||||
q1.template get<0>(), q1.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double dist(double x1, double y1, double x2, double y2) {
|
||||
return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double innerProd(double x1, double y1, double x2, double y2, double x3,
|
||||
double y3) {
|
||||
double dx21 = x2 - x1;
|
||||
double dx31 = x3 - x1;
|
||||
double dy21 = y2 - y1;
|
||||
double dy31 = y3 - y1;
|
||||
double m12 = sqrt(dx21 * dx21 + dy21 * dy21);
|
||||
double m13 = sqrt(dx31 * dx31 + dy31 * dy31);
|
||||
double theta = acos(std::min((dx21 * dx31 + dy21 * dy31) / (m12 * m13), 1.0));
|
||||
|
||||
return theta * (180 / M_PI);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G>
|
||||
inline double innerProd(const G& a, const G& b, const G& c) {
|
||||
return innerProd(a.template get<0>(), a.template get<1>(),
|
||||
b.template get<0>(), b.template get<1>(),
|
||||
c.template get<0>(), c.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename GeometryA, typename GeometryB>
|
||||
inline double dist(const GeometryA& p1, const GeometryB& p2) {
|
||||
return bgeo::distance(p1, p2);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry>
|
||||
inline std::string getWKT(Geometry g) {
|
||||
std::stringstream ss;
|
||||
ss << bgeo::wkt(g);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry>
|
||||
inline double len(Geometry g) {
|
||||
return bgeo::length(g);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry>
|
||||
inline Geometry simplify(Geometry g, double d) {
|
||||
Geometry ret;
|
||||
bgeo::simplify(g, ret, d);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double distToSegment(double lax, double lay, double lbx, double lby,
|
||||
double px, double py) {
|
||||
double d = dist(lax, lay, lbx, lby) * dist(lax, lay, lbx, lby);
|
||||
if (d == 0) return dist(px, py, lax, lay);
|
||||
|
||||
double t = ((px - lax) * (lbx - lax) + (py - lay) * (lby - lay)) / d;
|
||||
|
||||
if (t < 0) {
|
||||
return dist(px, py, lax, lay);
|
||||
} else if (t > 1) {
|
||||
return dist(px, py, lbx, lby);
|
||||
}
|
||||
|
||||
return dist(px, py, lax + t * (lbx - lax), lay + t * (lby - lay));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double distToSegment(const Point<T>& la, const Point<T>& lb,
|
||||
const Point<T>& p) {
|
||||
return distToSegment(la.template get<0>(), la.template get<1>(),
|
||||
lb.template get<0>(), lb.template get<1>(),
|
||||
p.template get<0>(), p.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> projectOn(const Point<T>& a, const Point<T>& b,
|
||||
const Point<T>& c) {
|
||||
if (doubleEq(a.template get<0>(), b.template get<0>()) &&
|
||||
doubleEq(a.template get<1>(), b.template get<1>()))
|
||||
return a;
|
||||
if (doubleEq(a.template get<0>(), c.template get<0>()) &&
|
||||
doubleEq(a.template get<1>(), c.template get<1>()))
|
||||
return a;
|
||||
if (doubleEq(b.template get<0>(), c.template get<0>()) &&
|
||||
doubleEq(b.template get<1>(), c.template get<1>()))
|
||||
return b;
|
||||
|
||||
double x, y;
|
||||
|
||||
if (c.template get<0>() == a.template get<0>()) {
|
||||
// infinite slope
|
||||
x = a.template get<0>();
|
||||
y = b.template get<1>();
|
||||
} else {
|
||||
double m = (double)(c.template get<1>() - a.template get<1>()) /
|
||||
(c.template get<0>() - a.template get<0>());
|
||||
double bb = (double)a.template get<1>() - (m * a.template get<0>());
|
||||
|
||||
x = (m * b.template get<1>() + b.template get<0>() - m * bb) / (m * m + 1);
|
||||
y = (m * m * b.template get<1>() + m * b.template get<0>() + bb) /
|
||||
(m * m + 1);
|
||||
}
|
||||
|
||||
Point<T> ret = Point<T>(x, y);
|
||||
|
||||
bool isBetween = dist(a, c) > dist(a, ret) && dist(a, c) > dist(c, ret);
|
||||
bool nearer = dist(a, ret) < dist(c, ret);
|
||||
|
||||
if (!isBetween) return nearer ? a : c;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double parallelity(const Box<T>& box, const Line<T>& line) {
|
||||
double ret = M_PI;
|
||||
|
||||
double a = angBetween(box.min_corner(),
|
||||
Point<T>(box.min_corner().template get<0>(),
|
||||
box.max_corner().template get<1>()));
|
||||
double b = angBetween(box.min_corner(),
|
||||
Point<T>(box.max_corner().template get<0>(),
|
||||
box.min_corner().template get<1>()));
|
||||
double c = angBetween(box.max_corner(),
|
||||
Point<T>(box.min_corner().template get<0>(),
|
||||
box.max_corner().template get<1>()));
|
||||
double d = angBetween(box.max_corner(),
|
||||
Point<T>(box.max_corner().template get<0>(),
|
||||
box.min_corner().template get<1>()));
|
||||
|
||||
double e = angBetween(line.front(), line.back());
|
||||
|
||||
double vals[] = {a, b, c, d};
|
||||
|
||||
for (double ang : vals) {
|
||||
double v = fabs(ang - e);
|
||||
if (v > M_PI) v = 2 * M_PI - v;
|
||||
if (v > M_PI / 2) v = M_PI - v;
|
||||
if (v < ret) ret = v;
|
||||
}
|
||||
|
||||
return 1 - (ret / (M_PI / 4));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double parallelity(const Box<T>& box, const MultiLine<T>& multiline) {
|
||||
double ret = 0;
|
||||
for (const Line<T>& l : multiline) {
|
||||
ret += parallelity(box, l);
|
||||
}
|
||||
|
||||
return ret / multiline.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename GeomA, typename GeomB>
|
||||
inline bool intersects(const GeomA& a, const GeomB& b) {
|
||||
return bgeo::intersects(a, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T, template <typename> typename Geometry>
|
||||
inline RotatedBox<T> getOrientedEnvelope(Geometry<T> pol) {
|
||||
// TODO: implement this nicer, works for now, but inefficient
|
||||
// see
|
||||
// https://geidav.wordpress.com/tag/gift-wrapping/#fn-1057-FreemanShapira1975
|
||||
// for a nicer algorithm
|
||||
|
||||
Point<T> center;
|
||||
bgeo::centroid(pol, center);
|
||||
|
||||
Box<T> tmpBox = getBoundingBox(pol);
|
||||
double rotateDeg = 0;
|
||||
|
||||
// rotate in 5 deg steps
|
||||
for (int i = 1; i < 360; i += 1) {
|
||||
pol = rotate(pol, 1, center);
|
||||
Box<T> e;
|
||||
bgeo::envelope(pol, e);
|
||||
if (bgeo::area(tmpBox) > bgeo::area(e)) {
|
||||
tmpBox = e;
|
||||
rotateDeg = i;
|
||||
}
|
||||
}
|
||||
|
||||
return RotatedBox<T>(tmpBox, -rotateDeg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> getBoundingBox(Point<T> pol) {
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
return tmpBox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> getBoundingBox(Line<T> pol) {
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
return tmpBox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> getBoundingBox(Polygon<T> pol) {
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
return tmpBox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> extendBox(const Box<T>& a, Box<T> b) {
|
||||
bgeo::expand(b, a);
|
||||
return b;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G, typename T>
|
||||
inline Box<T> extendBox(G pol, Box<T> b) {
|
||||
Box<T> tmp;
|
||||
bgeo::envelope(pol, tmp);
|
||||
bgeo::expand(b, tmp);
|
||||
return b;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double commonArea(const Box<T>& ba, const Box<T>& bb) {
|
||||
T l = std::max(ba.min_corner().template get<0>(),
|
||||
bb.min_corner().template get<0>());
|
||||
T r = std::min(ba.max_corner().template get<0>(),
|
||||
bb.max_corner().template get<0>());
|
||||
T b = std::max(ba.min_corner().template get<1>(),
|
||||
bb.min_corner().template get<1>());
|
||||
T t = std::min(ba.max_corner().template get<1>(),
|
||||
bb.max_corner().template get<1>());
|
||||
|
||||
if (l > r || b > t) return 0;
|
||||
|
||||
return (r - l) * (t - b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T, template <typename> typename Geometry>
|
||||
inline RotatedBox<T> getFullEnvelope(Geometry<T> pol) {
|
||||
Point<T> center;
|
||||
bgeo::centroid(pol, center);
|
||||
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
double rotateDeg = 0;
|
||||
|
||||
MultiPolygon<T> ml;
|
||||
|
||||
// rotate in 5 deg steps
|
||||
for (int i = 1; i < 360; i += 1) {
|
||||
pol = rotate(pol, 1, center);
|
||||
Polygon<T> hull;
|
||||
bgeo::convex_hull(pol, hull);
|
||||
ml.push_back(hull);
|
||||
Box<T> e;
|
||||
bgeo::envelope(pol, e);
|
||||
if (bgeo::area(tmpBox) > bgeo::area(e)) {
|
||||
tmpBox = e;
|
||||
rotateDeg = i;
|
||||
}
|
||||
}
|
||||
|
||||
bgeo::envelope(ml, tmpBox);
|
||||
|
||||
return RotatedBox<T>(tmpBox, rotateDeg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline RotatedBox<T> getOrientedEnvelopeAvg(MultiLine<T> ml) {
|
||||
MultiLine<T> orig = ml;
|
||||
// get oriented envelope for hull
|
||||
RotatedBox<T> rbox = getFullEnvelope(ml);
|
||||
Point<T> center;
|
||||
bgeo::centroid(rbox.b, center);
|
||||
|
||||
ml = rotate(ml, -rbox.rotateDeg - 45, center);
|
||||
|
||||
double bestDeg = -45;
|
||||
double score = parallelity(rbox.b, ml);
|
||||
|
||||
for (double i = -45; i <= 45; i += .5) {
|
||||
ml = rotate(ml, -.5, center);
|
||||
double p = parallelity(rbox.b, ml);
|
||||
if (parallelity(rbox.b, ml) > score) {
|
||||
bestDeg = i;
|
||||
score = p;
|
||||
}
|
||||
}
|
||||
|
||||
rbox.rotateDeg += bestDeg;
|
||||
|
||||
// move the box along 45deg angles from its origin until it fits the ml
|
||||
// = until the intersection of its hull and the box is largest
|
||||
Polygon<T> p = rbox.getPolygon();
|
||||
p = rotate(p, -rbox.rotateDeg, rbox.center);
|
||||
|
||||
Polygon<T> hull;
|
||||
bgeo::convex_hull(orig, hull);
|
||||
hull = rotate(hull, -rbox.rotateDeg, rbox.center);
|
||||
|
||||
Box<T> box;
|
||||
bgeo::envelope(hull, box);
|
||||
rbox = RotatedBox<T>(box, rbox.rotateDeg, rbox.center);
|
||||
|
||||
return rbox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Line<T> densify(const Line<T>& l, double d) {
|
||||
if (!l.size()) return l;
|
||||
|
||||
Line<T> ret;
|
||||
ret.reserve(l.size());
|
||||
ret.push_back(l.front());
|
||||
|
||||
for (size_t i = 1; i < l.size(); i++) {
|
||||
double segd = dist(l[i-1], l[i]);
|
||||
double dx =
|
||||
(l[i].template get<0>() - l[i-1].template get<0>()) / segd;
|
||||
double dy =
|
||||
(l[i].template get<1>() - l[i-1].template get<1>()) / segd;
|
||||
double curd = d;
|
||||
while (curd < segd) {
|
||||
ret.push_back(Point<T>(l[i-1].template get<0>() + dx * curd,
|
||||
l[i-1].template get<1>() + dy * curd));
|
||||
curd += d;
|
||||
}
|
||||
|
||||
ret.push_back(l[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double frechetDistC(size_t i, size_t j, const Line<T>& p,
|
||||
const Line<T>& q,
|
||||
std::vector<std::vector<double>>& ca) {
|
||||
// based on Eiter / Mannila
|
||||
// http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf
|
||||
|
||||
if (ca[i][j] > -1)
|
||||
return ca[i][j];
|
||||
else if (i == 0 && j == 0)
|
||||
ca[i][j] = dist(p[0], q[0]);
|
||||
else if (i > 0 && j == 0)
|
||||
ca[i][j] = std::max(frechetDistC(i - 1, 0, p, q, ca), dist(p[i], q[0]));
|
||||
else if (i == 0 && j > 0)
|
||||
ca[i][j] = std::max(frechetDistC(0, j - 1, p, q, ca), dist(p[0], q[j]));
|
||||
else if (i > 0 && j > 0)
|
||||
ca[i][j] = std::max(std::min(std::min(frechetDistC(i - 1, j, p, q, ca),
|
||||
frechetDistC(i - 1, j - 1, p, q, ca)),
|
||||
frechetDistC(i, j - 1, p, q, ca)),
|
||||
dist(p[i], q[j]));
|
||||
else
|
||||
ca[i][j] = std::numeric_limits<double>::infinity();
|
||||
|
||||
return ca[i][j];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double frechetDist(const Line<T>& a, const Line<T>& b, double d) {
|
||||
// based on Eiter / Mannila
|
||||
// http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf
|
||||
|
||||
auto p = densify(a, d);
|
||||
auto q = densify(b, d);
|
||||
|
||||
std::vector<std::vector<double>> ca(p.size(),
|
||||
std::vector<double>(q.size(), -1.0));
|
||||
double fd = frechetDistC(p.size() - 1, q.size() - 1, p, q, ca);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double accFrechetDistC(const Line<T>& a, const Line<T>& b, double d) {
|
||||
|
||||
auto p = densify(a, d);
|
||||
auto q = densify(b, d);
|
||||
|
||||
std::vector<std::vector<double>> ca(p.size(),
|
||||
std::vector<double>(q.size(), 0));
|
||||
|
||||
for (size_t i = 0; i < p.size(); i++) ca[i][0] = std::numeric_limits<double>::infinity();
|
||||
for (size_t j = 0; j < q.size(); j++) ca[0][j] = std::numeric_limits<double>::infinity();
|
||||
ca[0][0] = 0;
|
||||
|
||||
for (size_t i = 1; i < p.size(); i++) {
|
||||
for (size_t j = 1; j < q.size(); j++) {
|
||||
double d = util::geo::dist(p[i], q[j]) * util::geo::dist(p[i], p[i-1]);
|
||||
ca[i][j] = d + std::min(ca[i-1][j], std::min(ca[i][j-1], ca[i-1][j-1]));
|
||||
}
|
||||
}
|
||||
|
||||
return ca[p.size() - 1][q.size() - 1];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> latLngToWebMerc(double lat, double lng) {
|
||||
double x = 6378137.0 * lng * 0.017453292519943295;
|
||||
double a = lat * 0.017453292519943295;
|
||||
|
||||
double y = 3189068.5 * log((1.0 + sin(a)) / (1.0 - sin(a)));
|
||||
return Point<T>(x, y);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> webMercToLatLng(double x, double y) {
|
||||
double lat = 114.591559026 * (atan(exp(y / 6378137.0)) - 0.78539825);
|
||||
double lon = x / 111319.4907932735677;
|
||||
return Point<T>(lon, lat);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G1, typename G2>
|
||||
inline double webMercMeterDist(const G1& a, const G2& b) {
|
||||
// euclidean distance on web mercator is in meters on equator,
|
||||
// and proportional to cos(lat) in both y directions
|
||||
|
||||
double latA = 2 * atan(exp(a.template get<1>() / 6378137.0)) - 1.5707965;
|
||||
double latB = 2 * atan(exp(b.template get<1>() / 6378137.0)) - 1.5707965;
|
||||
|
||||
return util::geo::dist(a, b) * cos((latA + latB) / 2.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GEO_GEO_H_
|
||||
|
|
@ -1,31 +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"
|
||||
|
||||
namespace util {
|
||||
namespace geograph {
|
||||
|
||||
template<typename T>
|
||||
class GeoEdgePL {
|
||||
public:
|
||||
virtual const util::geo::Line<T>* getGeom() const = 0;
|
||||
virtual void getAttrs(std::map<std::string, std::string>* m) const = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class GeoNodePL {
|
||||
public:
|
||||
virtual const util::geo::Point<T>* getGeom() const = 0;
|
||||
virtual void getAttrs(std::map<std::string, std::string>* m) const = 0;
|
||||
};
|
||||
|
||||
} // namespace geograph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GEOGRAPH_H_
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue