Compare commits

..

No commits in common. "main" and "derhuerst-docker-image" have entirely different histories.

142 changed files with 28508 additions and 8639 deletions

View file

@ -1,4 +1,4 @@
/build
/Dockerfile
gtfs-out
/push-docker-image.sh

View file

@ -1,33 +0,0 @@
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

5
.gitmodules vendored
View file

@ -1,12 +1,9 @@
[submodule "src/cppgtfs"]
path = src/cppgtfs
url = https://github.com/ad-freiburg/cppgtfs.git
url = https://ad-git.informatik.uni-freiburg.de/ad/cppgtfs.git
[submodule "src/xml"]
path = src/xml
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

37
.travis.yml Normal file
View file

@ -0,0 +1,37 @@
language: cpp
os:
- linux
- osx
compiler:
- gcc
- clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- cmake
before_install:
- export LD_LIBRARY_PATH=$(if [[ $CXX == "clang++" ]]; then echo -n '/usr/local/clang/lib';
fi)
before_script:
- mkdir build
- cd build
- cmake ..
script:
- make -j4
- ctest --verbose
- docker build -t ad-freiburg/pfaedle .
deploy:
on:
branch: master
provider: script
script: bash push-docker-image.sh
notifications:
email:
on_success: never
on_failure: always
env:
global:
- secure: W60bRWv9u28UFjmt1iO5ELtPBvUNkAPqiwwxd+boy7BazAJ0fUUBZoEpnsgv8pqKWJV6VMUvknP4taU5a6NM+3aRHuCZOjVC42Rs8oDGJoXrhmH9ZzOOp2nDnHy9hqrtRKJrYQUww+s7UjBpWcaorqHvo5iWNrt9OulKM+V2u6IQI3xI1bPoaVhK/EnHHFAWe52v0KOkaSjguL5zj7xZqCeaZKmX9PsiQdqQJVtX2zsdF/aDkDvhkAl4SxeVKrFEVDV4gPx7yqGC/uQ6YJrQXigqpWWL6oZ1cxsg2HWqLZyAYN8tIWcnaAW8+PVYLfH1iTDb6fnokD4DPpVfULz4dzqOnTuG+Qd97U2BVDJN+LdxK2d8RZ1KLAWNbFGBlkY/8zpMAtV/xhGk2vHg/pj6ZPUPncNlOzUepASw5yCY7H6SOH1NgzNNSn+Fg3Q+6eUoYWp5jrpejBcwO/tikRCfGOSNyEKTC+1joRNwySeLjTcLDcaO3+EJL64YjIKW1+YwVFopq5DKRhzjSyO+dUryA5+l+nT499BC9dxA7SvQ/tLwMs3uPlVhSDUvkH6DxiWIJQEYmTZIEA4JmLjdBFDB9FaApubvn5wRIlL07Dbq+t3XxMciWeLU3H2IQzlGPQpIbqB93L8yc6eH5Fq2Gu4HN5lmpJC3ZLpZOEqNk1kcFzo=
- secure: b10JKrMacKD+C8yGHPiOYP84ykXqBFiwm2wkcKn9SkGO6bDCtRIM+/eYMN64wQVD54Vwc/rfxqCRN6ckdAgw1zrtBdlRLz1krdwsdb7RuXJvTTTu3bd8CrtlsvcVh40vcItcFAQQKhKrPF/iDajbXm4GaVcoiqV8i8inhhfg063guC22o3D76J3xFmwyhNGv0QZuK7xG0O2h+mflU/LE8FuXrVO4+1QmvwJ9JRgBnz5F8jrEuZWipp3gJBVnpYHv4ZAy5r52zQ3iPEkji7Y6/GjvxYnjc08QM998I3SSlUuW4quiEPJFTGxK9w/UV34c0DJhzluJ3TGTz+RkhejIDUcNiKqFKWZCzCwzcx5f96RmTh7MTHulB7zTkK0hzSSPFjrYrRrkN/FCwMrHaLs1H3SQbXiB2Ga2pnfaOVfCbM5KraZHlk2xHUIqVHkhyetETBW76d4g6vxjNoe++siyx+eUW2VMj7Y+6c1HQPceGi+jpl9pJK6ZXKfVpfWjFj29qqnv7lNjoI3PXGllswDV4KxP/A5A4MBqHAcVOFEdTro5EReUhepyNYM7lUaS//Wa6DzE6US13bVpIr4fC+bKUf8XizfGp+f9TSc+Shl0J6asejxIbgQRCopTJd805xAFoCsoK1yt/sZIplBO/mjaBjLc9Y1+A04VH5FQWl92rHQ=

View file

@ -1,23 +1,37 @@
cmake_minimum_required (VERSION 3.5)
set(CMAKE_CXX_STANDARD 11)
cmake_minimum_required (VERSION 2.8)
project (pfaedle)
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}")
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "DEBUG")
set(CPPLINT "${CMAKE_SOURCE_DIR}/cpplint.py")
include(cmake/cpplint.cmake)
endif()
set(CPPLINT_PROJECT_ROOT "src")
enable_testing()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}")
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build")
find_package(OpenMP)
if (OPENMP_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()
# set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake
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")
if(OPENMP_FOUND)
set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic")
else()
message(WARNING "Configuring without OpenMP!")
set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wextra -Wno-implicit-fallthrough -pedantic")
endif()
set(CMAKE_CXX_FLAGS_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")
@ -26,28 +40,24 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -g -DLOGLEVEL=3")
# export compile commands to tools like clang
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Compiler-specific C++11 activation.
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
if ((GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else ()
message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.8 or greater!")
endif ()
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else ()
message(FATAL_ERROR "Your C++ compiler does not support C++11.")
endif ()
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 ()
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)
git_get_tag(VERSION_GIT)
@ -71,7 +81,14 @@ add_subdirectory(src)
# tests
add_test("utilTest" utilTest)
add_test("pfaedleTest" pfaedleTest)
# custom eval target
add_custom_target(
eval
COMMAND make
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval
)
# handles install target
install(
@ -79,11 +96,6 @@ install(
)
install(
FILES ${CMAKE_BINARY_DIR}/pfaedle DESTINATION bin
PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries
)
install(
FILES ${CMAKE_BINARY_DIR}/shapevl DESTINATION bin
FILES build/pfaedle DESTINATION bin
PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE COMPONENT binaries
)

View file

@ -1,9 +1,9 @@
FROM debian:bookworm-slim AS builder
FROM debian:buster-slim AS builder
WORKDIR /app
RUN apt-get update && \
apt-get install -y g++ cmake git libzip-dev zlib1g-dev libbz2-dev
apt-get install -y g++ cmake git
ADD . /app
RUN mkdir build && \
@ -13,10 +13,10 @@ RUN mkdir build && \
pwd && \
make install
FROM debian:bookworm-slim
FROM debian:buster-slim
RUN apt-get update && \
apt-get install -y libzip4 zlib1g libbz2-1.0 && \
apt-get install -y libgomp1 && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/etc/pfaedle /usr/local/etc/pfaedle

View file

@ -1,51 +1,47 @@
[![Left: station-to-station path of a single train through Switzerland obtained from schedule timetable data. Right: path of the same train map-matched by pfaedle.](geo/schweiz_ex_res.png?raw=true)](geo/schweiz_ex.png?raw=true)
[![Left: station-to-station path of a single train through Switzerland obtained from schedule timetable data. Right: path of the same train map-matched by pfaedle.](geo/schweiz_ex_res.png?raw=true)](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.*
[![Left: station-to-station path of a single bus through Stuttgart obtained from official schedule data. Right: path of the same bus map-matched by pfaedle.](geo/stuttgart_ex_res.png?raw=true)](geo/stuttgart_ex.png?raw=true)
[![Left: station-to-station path of a single bus through Stuttgart obtained from official schedule data. Right: path of the same bus map-matched by pfaedle.](geo/stuttgart_ex_res.png?raw=true)](geo/stuttgart_ex.png?raw=true)
*Left: station-to-station path of a single bus through Stuttgart obtained from official schedule data. Right: path of the same bus map-matched by pfaedle.*
[![Build](https://github.com/ad-freiburg/pfaedle/actions/workflows/build.yml/badge.svg)](https://github.com/ad-freiburg/pfaedle/actions/workflows/build.yml)
[![Build
Status](https://travis-ci.org/ad-freiburg/pfaedle.svg?branch=master)](https://travis-ci.org/ad-freiburg/pfaedle)
# pfaedle
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).
Implementation and evaluation code for our paper [Sparse Map-Matching in Public Transit Networks with Turn Restrictions](http://ad-publications.informatik.uni-freiburg.de/SIGSPATIAL_Sparse%20map%20matching%202018.pdf).
## Requirements
* `cmake`
* `gcc >= 5.0` (or `clang >= 3.9`)
* `libzip` (*optional*, for ZIP support)
* `zlib` (*optional*, for gzip support)
* `libbz2` (*optional*, for bzip2 support)
* `gcc >= 4.9` (or `clang >= 5.0`)
## Building and Installation
Fetch this repository and init submodules:
```shell
$ git clone --recurse-submodules https://github.com/ad-freiburg/pfaedle
```
git clone --recurse-submodules https://github.com/ad-freiburg/pfaedle
```
```shell
$ mkdir build && cd build
$ cmake ..
$ make -j
```
mkdir build && cd build
cmake ..
make -j
```
To install, type
```shell
$ make install
```
make install
```
# General Usage
## Generating shapes for a GTFS feed
```shell
$ pfaedle -x <OSM FILE> <GTFS INPUT FEED>
```
pfaedle -x <OSM FILE> <GTFS INPUT FOLDER>
```
A shape'd version of the input GTFS feed will be written to `./gtfs-out`.
@ -53,19 +49,19 @@ A shape'd version of the input GTFS feed will be written to `./gtfs-out`.
By default, shapes are only calculated for trips that don't have a shape in the
input feed. To drop all existing shapes, use the `-D` flag.
For example, you may generate (and replace existing, see `-D` flag) shapes for the GTFS dataset for Freiburg like this:
For example, you may generate (and replace existing, see -D parameter) shapes for the GTFS dataset for Freiburg like this:
```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
```
$ wget https://fritz.freiburg.de/csv_Downloads/VAGFR.zip && unzip VAGFR.zip
$ wget http://download.geofabrik.de/europe/germany/baden-wuerttemberg/freiburg-regbez-latest.osm.bz2 && bunzip2 freiburg-regbez-latest.osm.bz2
$ pfaedle -D -x freiburg-regbez-latest.osm .
```
## Generating shapes for a specific MOT
To generate shapes for a specific mot only, use the `-m` option. Possible
values are either `tram`, `bus`, `coach`, `rail`, `subway`, `ferry`, `funicular`,
`gondola`, `all` (default) or GTFS 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`, ...).
`gondola`, `all` (default) or GTFS vehicle type codes (0, 1, 2, 3, 4, 5, 6, 7).
Multiple values can be specified (comma separated).
@ -81,20 +77,16 @@ 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:
You can use the [`ad-freiburg/pfaedle` Docker image](https://hub.docker.com/repository/docker/ad-freiburg/pfaedle) by mounting the OSM & GTFS data into the container:
```shell
$ docker pull ghcr.io/ad-freiburg/pfaedle:latest
$ docker run -i --rm \
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
pfaedle -x /osm/osm-data.xml -i /gtfs
```
## Debugging
@ -104,12 +96,48 @@ The following flags may be useful for debugging:
* `-T <GTFS TRIP ID>` only calculate shape for a single trip (specified via its GTFS trip id) and output it as GeoJSON to
`<dbg-path>/path.json`
* `--write-graph` write the graph used for routing as GeoJSON to
`<dbg-path>/graph.json`
* `--write-cgraph` if `-T` is set, write the combination graph used for
routing as GeoJSON to `<dbg-path>/combgraph.json`
* `--write-trgraph` write the complete network graph to `<dbg-path>/trgraph.json`
# Configuration
A default configuration file `pfaedle.cfg` can be found in this repo and will be installed with `make install`. Custom configuration files can be specified with the `-c` flag. If no `-c` flag is set, `pfaedle` will parse and merge the following cfg files in the given order (if present): `<install prefix>/etc/pfaedle/pfaedle.cfg`, `$HOME/.config/pfaedle/pfaedle.cfg`, `<CWD>/pfaedle.cfg`. Values given in later files will overwrite earlier defined values.
# Attribution
# Evaluation
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.
You may run an entire evaluation of our testing datasets Vitoria-Gasteiz, Paris, Switzerland and
Stuttgart with
```
mkdir build && cd build
cmake ..
make -j
make eval
```
*Notes:*
* this will download, and filter, the entire OSM files for Spain and the
Stuttgart region. Make sure you have enough space left on your hard drive.
* in evaluation mode, pfaedle needs significantly more time, because the
calculation of the similarity measurements between shapes are expensive
* if you are only interested in the end results of a single dataset, run
`make <dataset>.lighteval` in `/eval`. For example, `make paris.lighteval`
generates a shaped version of the paris dataset, without doing extensive
comparisons to the ground truth.
* similarily, if you want to run the extensive evaluation for a single dataset,
run `make <dataset>.eval` in `/eval`.
## Evaluation requirements
* zlib
On Debianesque systems, type
```
sudo apt-get install zlib1g-dev
```
to install the dependencies.

View file

@ -1,30 +0,0 @@
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 }}

View file

@ -1,52 +0,0 @@
# 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)

133
cmake/cpplint.cmake Normal file
View file

@ -0,0 +1,133 @@
#
# 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 Executable file

File diff suppressed because it is too large Load diff

133
eval/Makefile Normal file
View file

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

View file

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

1002
eval/eval.cfg Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

6
push-docker-image.sh Normal file
View file

@ -0,0 +1,6 @@
#!/bin/sh
set -ex
set -o pipefail
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker push adfreiburg/pfaedle

View file

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

@ -1 +1 @@
Subproject commit ca166b3446d5bb8b5fb8c6f637ca3f9cb0a8ff3b
Subproject commit b2d0c99b9c84f62f5f7b259524e0f4b1c9d38318

@ -1 +1 @@
Subproject commit d26d5794d396141905d71ecb8cd4f45e0120cba7
Subproject commit c704a610d91ec2be575c42a760cc6157c8fdf932

View file

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

View file

@ -17,18 +17,47 @@
#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
#if !defined(PFAEDLE_PRECISION) || (__str_d(PFAEDLE_PRECISION) == 1)
#undef PFAEDLE_PRECISION
#define PFAEDLE_PRECISION double
#endif
#define PFDL_PREC_STR __str_a(PFDL_PREC)
#define PFAEDLE_PRECISION_STR __str_a(PFAEDLE_PRECISION)
#define POINT util::geo::Point<PFDL_PREC>
#define LINE util::geo::Line<PFDL_PREC>
#define BOX util::geo::Box<PFDL_PREC>
#define POLYLINE util::geo::PolyLine<PFDL_PREC>
#define POINT util::geo::Point<PFAEDLE_PRECISION>
#define LINE util::geo::Line<PFAEDLE_PRECISION>
#define BOX util::geo::Box<PFAEDLE_PRECISION>
#define POLYLINE util::geo::PolyLine<PFAEDLE_PRECISION>
#define BOX_PADDING 2500
namespace pfaedle {
// _____________________________________________________________________________
inline std::string getTmpFName(std::string dir, std::string postf) {
if (postf.size()) postf = "-" + postf;
if (!dir.size()) dir = util::getTmpDir();
if (dir.size() && dir.back() != '/') dir = dir + "/";
std::string f = dir + ".pfaedle-tmp" + postf;
size_t c = 0;
while (access(f.c_str(), F_OK) != -1) {
c++;
if (c > 10000) {
// giving up...
LOG(ERROR) << "Could not find temporary file name!";
exit(1);
}
std::stringstream ss;
ss << dir << ".pfaedle-tmp" << postf << "-" << std::rand();
f = ss.str().c_str();
}
return f;
}
} // namespace pfaedle
#endif // PFAEDLE_DEF_H_

View file

@ -9,31 +9,28 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <clocale>
#include <fstream>
#include <map>
#include <string>
#include <vector>
#include "ad/cppgtfs/Parser.h"
#include "ad/cppgtfs/Writer.h"
#include "pfaedle/config/ConfigReader.h"
#include "pfaedle/config/MotConfig.h"
#include "pfaedle/config/MotConfigReader.h"
#include "pfaedle/eval/Collector.h"
#include "pfaedle/gtfs/Feed.h"
#include "pfaedle/gtfs/Writer.h"
#include "pfaedle/netgraph/Graph.h"
#include "pfaedle/osm/OsmIdSet.h"
#include "pfaedle/router/ShapeBuilder.h"
#include "pfaedle/router/Stats.h"
#include "pfaedle/statsimi-classifier/StatsimiClassifier.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/Misc.h"
#include "pfaedle/trgraph/StatGroup.h"
#include "util/geo/output/GeoGraphJsonOutput.h"
#include "util/geo/output/GeoJsonOutput.h"
#include "util/json/Writer.h"
#include "util/log/Log.h"
#include "util/Misc.h"
#ifndef CFG_HOME_SUFFIX
#define CFG_HOME_SUFFIX "/.config"
@ -45,30 +42,16 @@
#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::router::MOTs;
using pfaedle::osm::BBoxIdx;
using pfaedle::osm::OsmBuilder;
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::config::MotConfig;
using pfaedle::config::Config;
using pfaedle::router::ShapeBuilder;
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;
using configparser::ParseFileExc;
using pfaedle::config::MotConfigReader;
using pfaedle::config::ConfigReader;
using pfaedle::eval::Collector;
enum class RetCode {
SUCCESS = 0,
@ -86,9 +69,6 @@ enum class RetCode {
std::string getFileNameMotStr(const MOTs& mots);
std::vector<std::string> getCfgPaths(const Config& cfg);
// _____________________________________________________________________________
void gtfsWarnCb(std::string msg) { LOG(WARN) << msg; }
// _____________________________________________________________________________
int main(int argc, char** argv) {
// disable output buffering for standard output
@ -97,11 +77,6 @@ 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;
@ -109,18 +84,20 @@ int main(int argc, char** argv) {
cr.read(&cfg, argc, argv);
std::vector<pfaedle::gtfs::Feed> gtfs(cfg.feedPaths.size());
// feed containing the shapes in memory for evaluation
ad::cppgtfs::gtfs::Feed evalFeed;
std::vector<std::string> cfgPaths = getCfgPaths(cfg);
try {
motCfgReader.parse(cfgPaths, cfg.motCfgParam);
motCfgReader.parse(cfgPaths);
} 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) {
if (cfg.osmPath.empty() && !cfg.writeOverpass) {
std::cerr << "No OSM input file specified (-x), see --help." << std::endl;
exit(static_cast<int>(RetCode::NO_OSM_INPUT));
}
@ -131,42 +108,42 @@ int main(int argc, char** argv) {
exit(static_cast<int>(RetCode::NO_MOT_CFG));
}
T_START(gtfsBuild);
if (cfg.feedPaths.size() == 1) {
if (cfg.inPlace) cfg.outputPath = cfg.feedPaths[0];
if (!cfg.writeOverpass && !cfg.writeOsmfilter)
LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[0] << " ...";
if (!cfg.writeOverpass)
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
try {
ad::cppgtfs::Parser p(cfg.feedPaths[0], false,
cfg.parseAdditionalGTFSFields,
cfg.verbosity ? gtfsWarnCb : 0);
p.parse(&gtfs[0]);
ad::cppgtfs::Parser p;
p.parse(&gtfs[0], cfg.feedPaths[0]);
if (cfg.evaluate) {
// read the shapes and store them in memory
p.parseShapes(&evalFeed, cfg.feedPaths[0]);
}
} catch (const ad::cppgtfs::ParserException& ex) {
LOG(ERROR) << "Could not parse input GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_PARSE_ERR));
}
if (!cfg.writeOverpass) LOG(INFO) << "Done.";
} else if (cfg.writeOsm.size() || cfg.writeOverpass) {
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
if (!cfg.writeOverpass && !cfg.writeOsmfilter)
LOG(INFO) << "Reading GTFS feed " << cfg.feedPaths[i] << " ...";
if (!cfg.writeOverpass)
LOG(INFO) << "Reading " << cfg.feedPaths[i] << " ...";
ad::cppgtfs::Parser p;
try {
ad::cppgtfs::Parser p(cfg.feedPaths[i]);
p.parse(&gtfs[i]);
p.parse(&gtfs[i], cfg.feedPaths[i]);
} catch (const ad::cppgtfs::ParserException& ex) {
LOG(ERROR) << "Could not parse input GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_PARSE_ERR));
}
if (!cfg.writeOverpass) LOG(INFO) << "Done.";
}
} else if (cfg.feedPaths.size() > 1) {
std::cerr << "Multiple feeds only allowed in filter mode." << std::endl;
exit(static_cast<int>(RetCode::MULT_FEEDS_NOT_ALWD));
}
auto tGtfsBuild = T_STOP(gtfsBuild);
LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size()
<< " unique MOT configs.";
MOTs cmdCfgMots = cfg.mots;
@ -184,20 +161,12 @@ int main(int argc, char** argv) {
}
}
double maxSpeed = 0;
for (const auto& c : motCfgReader.getConfigs()) {
if (c.osmBuildOpts.maxSpeed > maxSpeed) {
maxSpeed = c.osmBuildOpts.maxSpeed;
}
}
if (cfg.writeOsm.size()) {
LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ...";
BBoxIdx box(BOX_PADDING);
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
&box, maxSpeed, 0, cfg.verbosity);
&box);
}
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
@ -219,7 +188,7 @@ int main(int argc, char** argv) {
BBoxIdx box(BOX_PADDING);
for (size_t i = 0; i < cfg.feedPaths.size(); i++) {
ShapeBuilder::getGtfsBox(&gtfs[i], cmdCfgMots, cfg.shapeTripId, true,
&box, maxSpeed, 0, cfg.verbosity);
&box);
}
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
@ -231,27 +200,15 @@ int main(int argc, char** argv) {
}
osmBuilder.overpassQryWrite(&std::cout, opts, box);
exit(static_cast<int>(RetCode::SUCCESS));
} else if (cfg.writeOsmfilter) {
BBoxIdx box(BOX_PADDING);
OsmBuilder osmBuilder;
std::vector<pfaedle::osm::OsmReadOpts> opts;
for (const auto& o : motCfgReader.getConfigs()) {
if (std::find_first_of(o.mots.begin(), o.mots.end(), cmdCfgMots.begin(),
cmdCfgMots.end()) != o.mots.end()) {
opts.push_back(o.osmBuildOpts);
}
}
osmBuilder.osmfilterRuleWrite(&std::cout, opts, box);
exit(static_cast<int>(RetCode::SUCCESS));
} else if (!cfg.feedPaths.size()) {
std::cout << "No input feed specified, see --help" << std::endl;
exit(static_cast<int>(RetCode::NO_INPUT_FEED));
}
Stats stats;
double tOsmBuild = 0;
std::map<std::string, std::pair<size_t, size_t>> graphDimensions;
std::vector<double> hopDists;
std::vector<double> dfBins;
auto dfBinStrings = util::split(std::string(cfg.evalDfBins), ',');
for (auto st : dfBinStrings) dfBins.push_back(atof(st.c_str()));
Collector ecoll(cfg.evalPath, dfBins);
for (const auto& motCfg : motCfgReader.getConfigs()) {
std::string filePost;
@ -263,7 +220,7 @@ int main(int argc, char** argv) {
filePost = getFileNameMotStr(usedMots);
std::string motStr = pfaedle::router::getMotStr(usedMots);
LOG(INFO) << "Matching shapes for mots " << motStr;
LOG(INFO) << "Calculating shapes for mots " << motStr;
try {
pfaedle::router::FeedStops fStops =
@ -274,108 +231,62 @@ int main(int argc, char** argv) {
pfaedle::osm::OsmBuilder osmBuilder;
pfaedle::osm::BBoxIdx box(BOX_PADDING);
ShapeBuilder::getGtfsBox(
&gtfs[0], usedMots, cfg.shapeTripId, cfg.dropShapes, &box,
motCfg.osmBuildOpts.maxSpeed, &hopDists, cfg.verbosity);
T_START(osmBuild);
ShapeBuilder::getGtfsBox(&gtfs[0], cmdCfgMots, cfg.shapeTripId,
cfg.dropShapes, &box);
if (fStops.size())
osmBuilder.read(cfg.osmPath, motCfg.osmBuildOpts, &graph, box,
cfg.gridSize, &restr);
cfg.gridSize, &fStops, &restr);
tOsmBuild += T_STOP(osmBuild);
graphDimensions[filePost].first = graph.getNds().size();
for (const auto& nd : graph.getNds()) {
graphDimensions[filePost].second += nd->getAdjListOut().size();
// TODO(patrick): move this somewhere else
for (auto& feedStop : fStops) {
if (feedStop.second) {
feedStop.second->pl().getSI()->getGroup()->writePens(
motCfg.osmBuildOpts.trackNormzer,
motCfg.routingOpts.platformUnmatchedPen,
motCfg.routingOpts.stationDistPenFactor,
motCfg.routingOpts.nonOsmPen);
}
}
StatsimiClassifier* statsimiClassifier;
if (motCfg.routingOpts.statsimiMethod == "bts") {
statsimiClassifier = new BTSClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "jaccard") {
statsimiClassifier = new JaccardClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "jaccard-geodist") {
statsimiClassifier = new JaccardGeodistClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "ed") {
statsimiClassifier = new EDClassifier();
} else if (motCfg.routingOpts.statsimiMethod == "ped") {
statsimiClassifier = new PEDClassifier();
} else {
LOG(ERROR) << "Unknown station similarity classifier "
<< motCfg.routingOpts.statsimiMethod;
exit(1);
}
Router* router = 0;
if (motCfg.routingOpts.transPenMethod == "exp") {
if (cfg.noAStar)
router = new RouterImpl<ExpoTransWeightNoHeur>();
else
router = new RouterImpl<ExpoTransWeight>();
} else if (motCfg.routingOpts.transPenMethod == "distdiff") {
if (cfg.noAStar)
router = new RouterImpl<DistDiffTransWeightNoHeur>();
else
router = new RouterImpl<DistDiffTransWeight>();
} else if (motCfg.routingOpts.transPenMethod == "timenorm") {
if (cfg.noAStar)
router = new RouterImpl<NormDistrTransWeightNoHeur>();
else
router = new RouterImpl<NormDistrTransWeight>();
} else {
LOG(ERROR) << "Unknown routing method "
<< motCfg.routingOpts.transPenMethod;
exit(1);
}
ShapeBuilder shapeBuilder(&gtfs[0], usedMots, motCfg, &graph, &fStops,
&restr, statsimiClassifier, router, cfg);
pfaedle::netgraph::Graph ng;
if (singleTrip) {
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
util::geo::output::GeoJsonOutput o(pstr);
auto l = shapeBuilder.shapeL(singleTrip);
stats += l.second;
LOG(INFO) << "Outputting path.json...";
// reproject to WGS84 to match RFC 7946
o.print(l.first, {});
o.flush();
pstr.close();
} else {
stats += shapeBuilder.shapeify(&ng);
}
if (router) delete router;
if (statsimiClassifier) delete statsimiClassifier;
ShapeBuilder shapeBuilder(&gtfs[0], &evalFeed, cmdCfgMots, motCfg, &ecoll,
&graph, &fStops, &restr, cfg);
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);
out.printLatLng(*shapeBuilder.getGraph(), fstr);
fstr.close();
}
if (singleTrip) exit(static_cast<int>(RetCode::SUCCESS));
if (singleTrip) {
LOG(INFO) << "Outputting path.json...";
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
util::geo::output::GeoJsonOutput o(pstr);
auto l = shapeBuilder.shapeL(singleTrip);
// reproject to WGS84 to match RFC 7946
o.printLatLng(l, {});
o.flush();
pstr.close();
exit(static_cast<int>(RetCode::SUCCESS));
}
pfaedle::netgraph::Graph ng;
shapeBuilder.shape(&ng);
if (cfg.buildTransitGraph) {
util::geo::output::GeoGraphJsonOutput out;
LOG(INFO) << "Outputting trgraph-" + filePost + ".json...";
LOG(INFO) << "Outputting trgraph" + filePost + ".json...";
mkdir(cfg.dbgOutputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph-" + filePost +
".json");
out.print(ng, fstr);
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph" + filePost + ".json");
out.printLatLng(ng, fstr);
fstr.close();
}
} catch (const pfxml::parse_exc& ex) {
@ -385,58 +296,16 @@ int main(int argc, char** argv) {
}
}
// 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.evaluate) ecoll.printStats(&std::cout);
if (cfg.feedPaths.size()) {
try {
mkdir(cfg.outputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
LOG(INFO) << "Writing output GTFS to " << cfg.outputPath << " ...";
pfaedle::gtfs::Writer w;
w.write(&gtfs[0], cfg.outputPath);
} catch (const ad::cppgtfs::WriterException& ex) {
LOG(ERROR) << "Could not write output GTFS feed, reason was:";
LOG(ERROR) << "Could not write final GTFS feed, reason was:";
std::cerr << ex.what() << std::endl;
exit(static_cast<int>(RetCode::GTFS_WRITE_ERR));
}
@ -447,27 +316,9 @@ int main(int argc, char** argv) {
// _____________________________________________________________________________
std::string getFileNameMotStr(const MOTs& mots) {
MOTs tmp = mots;
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 = 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);
for (const auto& mot : mots) {
motStr += "-" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot);
}
return motStr;
@ -478,10 +329,12 @@ std::vector<std::string> getCfgPaths(const Config& cfg) {
if (cfg.configPaths.size()) return cfg.configPaths;
std::vector<std::string> ret;
// install prefix global configuration path, if available
{
auto path = std::string(INSTALL_PREFIX) + std::string(CFG_DIR) + "/" +
"pfaedle" + "/" + CFG_FILE_NAME;
auto path = std::string(INSTALL_PREFIX) +
std::string(CFG_DIR) + "/" + "pfaedle" + "/" +
CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;
@ -493,8 +346,8 @@ std::vector<std::string> getCfgPaths(const Config& cfg) {
// local user configuration path, if available
{
auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" + "pfaedle" + "/" +
CFG_FILE_NAME;
auto path = util::getHomeDir() + CFG_HOME_SUFFIX + "/" +
"pfaedle" + "/" + CFG_FILE_NAME;
std::ifstream is(path);
LOG(DEBUG) << "Testing for config file at " << path;

View file

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

View file

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

View file

@ -2,33 +2,28 @@
// 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 ad::cppgtfs::gtfs::Route;
using configparser::ConfigFileParser;
using configparser::ParseExc;
using pfaedle::config::MotConfig;
using pfaedle::config::MotConfigReader;
using pfaedle::osm::DeepAttrRule;
using pfaedle::config::MotConfig;
using pfaedle::osm::FilterRule;
using pfaedle::osm::KeyVal;
using configparser::ConfigFileParser;
using configparser::ParseExc;
using pfaedle::osm::DeepAttrRule;
using pfaedle::trgraph::ReplRules;
double DEF_TRANS_PEN = 0.0083;
using ad::cppgtfs::gtfs::Route;
// _____________________________________________________________________________
MotConfigReader::MotConfigReader() {}
// _____________________________________________________________________________
void MotConfigReader::parse(const std::vector<std::string>& paths,
const std::string& literal) {
void MotConfigReader::parse(const std::vector<std::string>& paths) {
ConfigFileParser p;
// parse explicitely given paths
@ -37,47 +32,17 @@ void MotConfigReader::parse(const std::vector<std::string>& paths,
p.parse(s);
}
if (literal.size()) p.parseStr(literal);
for (const auto& sec : p.getSecs()) {
MotConfig cfg;
cfg.transWeight = "expo";
MotConfig curCfg;
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;
}
std::set<std::string> procedKeys;
if (p.hasKey(secStr, "osm_filter_keep")) {
procedKeys.insert("osm_filter_keep");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_keep", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
@ -85,468 +50,321 @@ void MotConfigReader::parse(const std::vector<std::string>& paths,
for (uint8_t i = 0; i < 8; i++) {
std::string name = std::string("osm_filter_lvl") + std::to_string(i);
if (p.hasKey(secStr, name)) {
procedKeys.insert(name);
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
}
if (p.hasKey(secStr, "osm_filter_drop")) {
procedKeys.insert("osm_filter_drop");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.dropFilter[fRule.kv.first].insert(
curCfg.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");
procedKeys.insert("osm_max_snap_level");
curCfg.osmBuildOpts.maxSnapLevel =
p.getInt(sec.first, "osm_max_snap_level");
} else {
cfg.osmBuildOpts.maxSnapLevel = 7;
curCfg.osmBuildOpts.maxSnapLevel = 7;
}
if (p.hasKey(secStr, "osm_filter_nohup")) {
procedKeys.insert("osm_filter_nohup");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_nohup", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
curCfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_oneway")) {
procedKeys.insert("osm_filter_oneway");
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_oneway", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_oneway_reverse")) {
procedKeys.insert("osm_filter_oneway_reverse");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
curCfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_undirected")) {
procedKeys.insert("osm_filter_undirected");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_undirected", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
curCfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_station")) {
procedKeys.insert("osm_filter_station");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_station", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
curCfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_station_blocker")) {
procedKeys.insert("osm_filter_station_blocker");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) {
auto fRule = getFRule(kvs);
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(
curCfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_node_positive_restriction")) {
procedKeys.insert("osm_node_positive_restriction");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
curCfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_node_negative_restriction")) {
procedKeys.insert("osm_node_negative_restriction");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
curCfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_filter_no_restriction")) {
procedKeys.insert("osm_filter_no_restriction");
for (const auto& kvs :
p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) {
auto fRule = getFRule(kvs);
cfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
curCfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
}
}
if (p.hasKey(secStr, "osm_station_name_attrs")) {
procedKeys.insert("osm_station_name_attrs");
for (const std::string& r :
p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) {
cfg.osmBuildOpts.statAttrRules.nameRule.push_back(getDeepAttrRule(r));
curCfg.osmBuildOpts.statAttrRules.nameRule.push_back(
getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_track_number_tags")) {
procedKeys.insert("osm_track_number_tags");
for (const std::string& r :
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
cfg.osmBuildOpts.statAttrRules.platformRule.push_back(
curCfg.osmBuildOpts.statAttrRules.platformRule.push_back(
getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_station_id_attrs")) {
procedKeys.insert("osm_station_id_attrs");
for (const std::string& r :
p.getStrArr(sec.first, "osm_station_id_attrs", ' ')) {
cfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r));
curCfg.osmBuildOpts.statAttrRules.idRule.push_back(getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_edge_track_number_tags")) {
procedKeys.insert("osm_edge_track_number_tags");
for (const std::string& r :
p.getStrArr(sec.first, "osm_edge_track_number_tags", ' ')) {
cfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
curCfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
}
}
if (p.hasKey(secStr, "osm_station_group_attrs")) {
LOG(WARN) << "Option osm_station_group_attrs has been removed.";
procedKeys.insert("osm_station_group_attrs");
auto arr = p.getStrArr(secStr, "osm_station_group_attrs", ' ');
for (const auto& ruleStr : arr) {
auto deep = getDeepAttrRule(ruleStr);
// TODO(patrick): getKv is misused here as a a=b parser
auto attrD = getKv(deep.attr);
deep.attr = attrD.first;
double dist = atof(attrD.second.c_str());
curCfg.osmBuildOpts.statGroupNAttrRules.push_back({deep, dist});
}
}
// default value, to enable color writing on old configs
cfg.osmBuildOpts.relLinerules.colorRule = {"colour", "color"};
if (p.hasKey(secStr, "osm_line_relation_tags")) {
procedKeys.insert("osm_line_relation_tags");
auto arr = p.getStrArr(secStr, "osm_line_relation_tags", ' ');
for (const auto& ruleStr : arr) {
auto rule = getKv(ruleStr);
auto tags = util::split(rule.second, ',');
if (rule.first == "from_name")
cfg.osmBuildOpts.relLinerules.fromNameRule = tags;
curCfg.osmBuildOpts.relLinerules.fromNameRule = tags;
else if (rule.first == "to_name")
cfg.osmBuildOpts.relLinerules.toNameRule = tags;
curCfg.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;
curCfg.osmBuildOpts.relLinerules.sNameRule = 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;
procedKeys.insert("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")) {
LOG(WARN) << "Option osm_max_snap_fallback_distance has been removed.";
procedKeys.insert("osm_max_snap_fallback_distance");
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
p.getDouble(secStr, "osm_max_snap_fallback_distance");
} else {
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.end()) *
2;
}
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);
procedKeys.insert("osm_max_osm_station_distance");
curCfg.osmBuildOpts.maxOsmStationDistance =
p.getDouble(secStr, "osm_max_osm_station_distance");
} else {
cfg.osmBuildOpts.maxOsmStationDistances.push_back(15);
curCfg.osmBuildOpts.maxOsmStationDistance = 5;
}
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
cfg.osmBuildOpts.maxBlockDistance =
procedKeys.insert("osm_max_node_block_distance");
curCfg.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()) /
curCfg.osmBuildOpts.maxBlockDistance =
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
curCfg.osmBuildOpts.maxSnapDistances.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
procedKeys.insert(name);
double v = p.getDouble(sec.first, name);
curCfg.routingOpts.levelPunish[i] = v;
} else {
curCfg.routingOpts.levelPunish[i] = 1;
}
}
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;
procedKeys.insert("routing_full_turn_punish");
curCfg.routingOpts.fullTurnPunishFac =
p.getDouble(secStr, "routing_full_turn_punish");
}
if (p.hasKey(secStr, "routing_no_self_hops")) {
cfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops");
procedKeys.insert("routing_no_self_hops");
curCfg.routingOpts.noSelfHops = p.getBool(secStr, "routing_no_self_hops");
}
if (p.hasKey(secStr, "routing_full_turn_angle")) {
double ang = p.getPosDouble(secStr, "routing_full_turn_angle");
cfg.routingOpts.fullTurnAngle = ang;
cfg.osmBuildOpts.fullTurnAngle = ang;
procedKeys.insert("routing_full_turn_angle");
double ang = p.getDouble(secStr, "routing_full_turn_angle");
curCfg.routingOpts.fullTurnAngle = ang;
curCfg.osmBuildOpts.fullTurnAngle = ang;
} else {
cfg.routingOpts.fullTurnAngle = 5;
cfg.osmBuildOpts.fullTurnAngle = 5;
curCfg.routingOpts.fullTurnAngle = 5;
curCfg.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;
procedKeys.insert("routing_snap_full_turn_angle");
double ang = p.getDouble(secStr, "routing_snap_full_turn_angle");
curCfg.osmBuildOpts.maxAngleSnapReach = ang;
} else {
cfg.osmBuildOpts.maxAngleSnapReach = cfg.routingOpts.fullTurnAngle;
curCfg.osmBuildOpts.maxAngleSnapReach = curCfg.routingOpts.fullTurnAngle;
}
if (p.hasKey(secStr, "routing_pass_thru_station_punish")) {
LOG(WARN) << "Option routing_pass_thru_station_punish has been removed.";
procedKeys.insert("routing_pass_thru_station_punish");
curCfg.routingOpts.passThruStationsPunish =
p.getDouble(secStr, "routing_pass_thru_station_punish");
}
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_one_way_meter_punish_fac")) {
procedKeys.insert("routing_one_way_meter_punish_fac");
curCfg.routingOpts.oneWayPunishFac =
p.getDouble(secStr, "routing_one_way_meter_punish_fac");
}
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
if (p.hasKey(secStr, "routing_one_way_edge_punish")) {
procedKeys.insert("routing_one_way_edge_punish");
curCfg.routingOpts.oneWayEdgePunish =
p.getDouble(secStr, "routing_one_way_edge_punish");
}
// 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)";
procedKeys.insert("routing_line_unmatched_punish_fac");
curCfg.routingOpts.lineUnmatchedPunishFact =
p.getDouble(secStr, "routing_line_unmatched_punish_fac");
}
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_no_lines_punish_fac")) {
procedKeys.insert("routing_no_lines_punish_fac");
curCfg.routingOpts.noLinesPunishFact =
p.getDouble(secStr, "routing_no_lines_punish_fac");
}
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
procedKeys.insert("routing_platform_unmatched_punish");
curCfg.routingOpts.platformUnmatchedPen =
p.getDouble(secStr, "routing_platform_unmatched_punish");
}
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)";
procedKeys.insert("routing_non_osm_station_punish");
curCfg.routingOpts.nonOsmPen =
p.getDouble(secStr, "routing_non_osm_station_punish");
} else {
cfg.routingOpts.nonStationPen = 0;
curCfg.routingOpts.nonOsmPen = 0;
}
if (p.hasKey(secStr, "routing_non_station_penalty")) {
cfg.routingOpts.nonStationPen =
p.getPosDouble(secStr, "routing_non_station_penalty");
if (p.hasKey(secStr, "routing_station_distance_punish_fac")) {
procedKeys.insert("routing_station_distance_punish_fac");
curCfg.routingOpts.stationDistPenFactor =
p.getDouble(secStr, "routing_station_distance_punish_fac");
} 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;
curCfg.routingOpts.stationDistPenFactor = 1;
}
if (p.hasKey(secStr, "station_normalize_chain")) {
procedKeys.insert("station_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
cfg.osmBuildOpts.statNormzer = trgraph::Normalizer(getNormRules(arr));
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,
@ -557,9 +375,11 @@ void MotConfigReader::parse(const std::vector<std::string>& paths,
}
if (p.hasKey(secStr, "track_normalize_chain")) {
procedKeys.insert("track_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
cfg.osmBuildOpts.trackNormzer = trgraph::Normalizer(getNormRules(arr));
curCfg.osmBuildOpts.trackNormzer =
trgraph::Normalizer(getNormRules(arr));
} catch (const std::exception& e) {
throw ParseExc(p.getVal(secStr, "track_normalize_chain").line,
p.getVal(secStr, "track_normalize_chain").pos,
@ -570,9 +390,11 @@ void MotConfigReader::parse(const std::vector<std::string>& paths,
}
if (p.hasKey(secStr, "line_normalize_chain")) {
procedKeys.insert("line_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
cfg.osmBuildOpts.lineNormzer = trgraph::Normalizer(getNormRules(arr));
curCfg.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,
@ -583,9 +405,10 @@ void MotConfigReader::parse(const std::vector<std::string>& paths,
}
if (p.hasKey(secStr, "station_id_normalize_chain")) {
procedKeys.insert("station_id_normalize_chain");
try {
auto arr = p.getStrArr(secStr, "station_id_normalize_chain", ';');
cfg.osmBuildOpts.idNormzer = trgraph::Normalizer(getNormRules(arr));
curCfg.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,
@ -595,41 +418,18 @@ void MotConfigReader::parse(const std::vector<std::string>& paths,
}
}
// 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];
for (const auto& kv : p.getKeyVals(secStr)) {
if (!procedKeys.count(kv.first))
curCfg.unproced[kv.first] = kv.second.val;
}
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& cfg : _cfgs) {
if (cfg == curCfg) {
for (auto mot :
ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr)) {
exCfg.mots.insert(mot);
cfg.mots.insert(mot);
}
found = true;
break;
@ -637,8 +437,8 @@ void MotConfigReader::parse(const std::vector<std::string>& paths,
}
if (!found) {
cfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr);
_cfgs.push_back(cfg);
curCfg.mots = ad::cppgtfs::gtfs::flat::Route::getTypesFromString(secStr);
_cfgs.push_back(curCfg);
}
}
}

View file

@ -23,7 +23,7 @@ using ad::cppgtfs::gtfs::Route;
class MotConfigReader {
public:
MotConfigReader();
void parse(const std::vector<std::string>& paths, const std::string& literal);
void parse(const std::vector<std::string>& paths);
const std::vector<MotConfig>& getConfigs() const;

View file

@ -5,13 +5,11 @@
#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 {
@ -22,53 +20,39 @@ struct Config {
Config()
: dbgOutputPath("."),
solveMethod("global"),
evalPath("."),
outputPath("gtfs-out"),
dropShapes(false),
useHMM(false),
writeGraph(false),
writeCombGraph(false),
evaluate(false),
buildTransitGraph(false),
useCaching(false),
writeOverpass(false),
writeOsmfilter(false),
inPlace(false),
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) {}
gridSize(2000) {}
std::string dbgOutputPath;
std::string solveMethod;
std::string evalPath;
std::string shapeTripId;
std::string outputPath;
std::string writeOsm;
std::string osmPath;
std::string motCfgParam;
std::string evalDfBins;
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;
@ -80,19 +64,10 @@ struct Config {
<< "drop-shapes: " << dropShapes << "\n"
<< "use-hmm: " << useHMM << "\n"
<< "write-graph: " << writeGraph << "\n"
<< "write-cgraph: " << writeCombGraph << "\n"
<< "grid-size: " << gridSize << "\n"
<< "use-cache: " << useCaching << "\n"
<< "write-overpass: " << writeOverpass << "\n"
<< "write-osmfilter: " << writeOsmfilter << "\n"
<< "inplace: " << inPlace << "\n"
<< "write-colors: " << writeColors << "\n"
<< "no-fast-hops: " << noFastHops << "\n"
<< "no-a-star: " << noAStar << "\n"
<< "no-trie: " << noTrie << "\n"
<< "no-hop-cache: " << noHopCache << "\n"
<< "verbosity: " << verbosity << "\n"
<< "parse-additional-gtfs-fields: " << parseAdditionalGTFSFields << "\n"
<< "write-stats: " << writeStats << "\n"
<< "feed-paths: ";
for (const auto& p : feedPaths) {

View file

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

View file

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

View file

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

View file

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

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

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

View file

@ -61,7 +61,6 @@ class ShapeContainer {
size_t _max;
std::string _curId;
std::stringstream _writeBuffer;
std::fpos<std::mbstate_t> _lastBuff;
};
#include "ShapeContainer.tpp"

View file

@ -2,20 +2,20 @@
// 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", "");
ShapeContainer<T>::ShapeContainer() {
std::string f = pfaedle::getTmpFName("", "");
_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;
std::cerr << "Could not open temporary file " << f
<< std::endl;
exit(1);
}
}
@ -23,8 +23,6 @@ ShapeContainer<T>::ShapeContainer() : _lastBuff(0) {
// ____________________________________________________________________________
template <typename T>
ShapeContainer<T>::~ShapeContainer() {
_storage << _writeBuffer.rdbuf();
_storage.flush();
_storage.close();
}
@ -45,15 +43,15 @@ bool ShapeContainer<T>::remove(const std::string& id) {
// ____________________________________________________________________________
template <typename T>
T* ShapeContainer<T>::get(const std::string& id) {
UNUSED(id);
return reinterpret_cast<T*>(0);
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
template <typename T>
const T* ShapeContainer<T>::get(const std::string& id) const {
UNUSED(id);
return reinterpret_cast<T*>(0);
if (!has(id)) return 0;
return reinterpret_cast<T*>(1);
}
// ____________________________________________________________________________
@ -72,22 +70,17 @@ size_t ShapeContainer<T>::size() const {
template <typename T>
std::string ShapeContainer<T>::add(const ad::cppgtfs::gtfs::Shape& s) {
if (has(s.getId())) return s.getId();
size_t size = s.getPoints().size();
_ids.insert(s.getId());
_writeBuffer << s.getId();
_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));
_writeBuffer << s.getId() << '\t' << s.getPoints().size();
_writeBuffer << std::setprecision(11);
for (auto p : s.getPoints()) {
_writeBuffer << " " << p.lat << " " << p.lng << " " << p.travelDist;
}
// entries are newline separated
_writeBuffer << '\n';
if (_writeBuffer.tellp() - _lastBuff > 1000 * 5000) {
_lastBuff = _writeBuffer.tellp();
if (_writeBuffer.tellp() > 1000 * 5000) {
_storage << _writeBuffer.rdbuf();
_writeBuffer.clear();
}
@ -99,7 +92,6 @@ std::string ShapeContainer<T>::add(const ad::cppgtfs::gtfs::Shape& s) {
template <typename T>
void ShapeContainer<T>::open() {
_storage << _writeBuffer.rdbuf();
_storage.flush();
_writeBuffer.clear();
_ptr = 0;
@ -115,18 +107,14 @@ bool ShapeContainer<T>::nextStoragePt(
while (_storage.good() && !_storage.fail()) {
if (!_ptr) {
_storage >> _curId;
_storage.ignore();
_storage.read(reinterpret_cast<char*>(&_max), sizeof(_max));
_storage >> _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));
_storage >> ret->lat;
_storage >> ret->lng;
_storage >> ret->travelDist;
ret->seq = _ptr + 1;
ret->id = _curId;

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -5,11 +5,8 @@
#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"
@ -30,7 +27,6 @@ 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;
@ -50,7 +46,7 @@ class OsmFilter {
private:
MultAttrMap _keep, _drop, _nohup, _oneway, _onewayrev, _twoway, _station,
_blocker, _posRestr, _negRestr, _noRestr, _turnCycle;
_blocker, _posRestr, _negRestr, _noRestr;
const MultAttrMap* _levels;
};
} // namespace osm

View file

@ -15,7 +15,6 @@
#include <string>
#include "pfaedle/Def.h"
#include "pfaedle/osm/OsmIdSet.h"
#include "util/3rdparty/MurmurHash3.h"
using pfaedle::osm::OsmIdSet;
@ -29,46 +28,26 @@ 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[BUFFER_S];
_outBuffer = new unsigned char[OBUFFER_S];
}
// _____________________________________________________________________________
OsmIdSet::~OsmIdSet() {
delete _bitset;
delete _bitsetNotIn;
delete[] _buffer;
if (!_closed) delete[] _outBuffer;
}
// _____________________________________________________________________________
void OsmIdSet::nadd(osmid id) {
if (_closed) throw std::exception();
_hasInv = true;
uint32_t h1, h2;
MurmurHash3_x86_32(&id, 8, 469954432, &h1);
h2 = jenkins(id);
for (int i = 0; i < 5; i++) {
uint32_t h = (h1 + i * h2) % BLOOMF_BITS;
(*_bitsetNotIn)[h] = 1;
}
}
// _____________________________________________________________________________
void OsmIdSet::add(osmid id) {
if (_closed) throw std::exception();
diskAdd(id);
if (_last > id) _sorted = false;
@ -76,14 +55,7 @@ void OsmIdSet::add(osmid id) {
if (id < _smallest) _smallest = id;
if (id > _biggest) _biggest = id;
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;
}
for (int i = 0; i < 10; i++) (*_bitset)[hash(id, i)] = 1;
}
// _____________________________________________________________________________
@ -97,8 +69,8 @@ void OsmIdSet::diskAdd(osmid id) {
_blockEnds.push_back(id);
}
if (_obufpos >= BUFFER_S) {
ssize_t w = cwrite(_file, _outBuffer, BUFFER_S);
if (_obufpos >= OBUFFER_S) {
ssize_t w = cwrite(_file, _outBuffer, OBUFFER_S);
_fsize += w;
_obufpos = 0;
}
@ -114,8 +86,7 @@ size_t OsmIdSet::getBlock(osmid id) const {
bool OsmIdSet::diskHas(osmid id) const {
assert(_sorted);
auto a = std::lower_bound(_blockEnds.begin(), _blockEnds.end(), id);
if (a != _blockEnds.end() && *a == id) {
if (std::find(_blockEnds.begin(), _blockEnds.end(), id) != _blockEnds.end()) {
return true;
}
@ -154,23 +125,12 @@ bool OsmIdSet::has(osmid id) const {
LOOKUPS++;
if (!_closed) close();
// trivial cases
if (id < _smallest || id > _biggest) {
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;
}
for (int i = 0; i < 10; i++) {
if ((*_bitset)[hash(id, i)] == 0) return false;
}
bool has = diskHas(id);
@ -289,8 +249,8 @@ size_t OsmIdSet::cread(int f, void* buf, size_t n) const {
// _____________________________________________________________________________
uint32_t OsmIdSet::knuth(uint32_t in) const {
const uint32_t a = 2654435769;
return (in * a) >> 2;
const uint32_t prime = 2654435769;
return (in * prime) >> 2;
}
// _____________________________________________________________________________
@ -304,9 +264,14 @@ 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 = util::getTmpFName("<tmp>", ".pfaedle-tmp", "");
const std::string& fname = getTmpFName("", "");
int file = open(fname.c_str(), O_RDWR | O_CREAT, 0666);
// immediately unlink

View file

@ -25,7 +25,7 @@ static const size_t BUFFER_S = 8 * 64 * 1024;
static const size_t SORT_BUFFER_S = 8 * 64 * 1024;
static const size_t OBUFFER_S = 8 * 1024 * 1024;
#define BLOOMF_BITS 214748357
#define BLOOMF_BITS 400000000
/*
* A disk-based set for OSM ids. Read-access for checking the presence is
@ -39,9 +39,6 @@ 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;
@ -60,8 +57,6 @@ class OsmIdSet {
osmid _smallest;
osmid _biggest;
bool _hasInv;
size_t _obufpos;
mutable size_t _curBlock;
mutable ssize_t _curBlockSize;
@ -69,14 +64,13 @@ class OsmIdSet {
// bloom filter
std::bitset<BLOOMF_BITS>* _bitset;
std::bitset<BLOOMF_BITS>* _bitsetNotIn;
mutable std::vector<osmid> _blockEnds;
mutable size_t _fsize;
uint32_t knuth(uint32_t in) const;
uint32_t jenkins(uint32_t in) const;
uint32_t hash(uint32_t in, int i) const;
void diskAdd(osmid id);
void close() const;
void sort() const;

View file

@ -5,14 +5,14 @@
#ifndef PFAEDLE_OSM_OSMREADOPTS_H_
#define PFAEDLE_OSM_OSMREADOPTS_H_
#include <map>
#include <queue>
#include <set>
#include <unordered_set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <utility>
#include <vector>
#include <set>
#include "pfaedle/osm/Osm.h"
#include "pfaedle/trgraph/Graph.h"
#include "pfaedle/trgraph/Normalizer.h"
@ -77,12 +77,11 @@ 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.colorRule == b.colorRule;
a.toNameRule == b.toNameRule;
}
struct StationAttrRules {
@ -95,6 +94,21 @@ 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() {}
@ -107,7 +121,7 @@ struct OsmReadOpts {
MultAttrMap twoWayFilter;
MultAttrMap stationFilter;
MultAttrMap stationBlockerFilter;
MultAttrMap turnCycleFilter;
std::vector<StatGroupNAttrRule> statGroupNAttrRules;
trgraph::Normalizer statNormzer;
trgraph::Normalizer lineNormzer;
@ -122,23 +136,14 @@ struct OsmReadOpts {
uint8_t maxSnapLevel;
double maxAngleSnapReach;
double maxSnapDistance;
double maxStationCandDistance;
std::vector<double> maxSnapDistances;
double maxSnapFallbackHeurDistance;
double maxBlockDistance;
double maxSpeed;
double maxSpeedCorFac;
double maxOsmStationDistance;
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;
// TODO(patrick): this is not implemented yet
double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0};
double fullTurnAngle;
@ -149,10 +154,9 @@ struct OsmReadOpts {
};
inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
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)
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)
return false;
}
@ -169,29 +173,24 @@ inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
a.twoWayFilter == b.twoWayFilter &&
a.stationFilter == b.stationFilter &&
a.stationBlockerFilter == b.stationBlockerFilter &&
a.turnCycleFilter == b.turnCycleFilter &&
a.statGroupNAttrRules == b.statGroupNAttrRules &&
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.maxSnapDistance - b.maxSnapDistance) < 0.1 &&
fabs(a.maxStationCandDistance - b.maxStationCandDistance) < 0.1 &&
fabs(a.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 &&
fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) <
0.1 &&
fabs(a.maxBlockDistance - b.maxBlockDistance) < 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.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.fullTurnAngle - b.fullTurnAngle) < 0.1 &&
fabs(a.maxSpeedCorFac - b.maxSpeedCorFac) < 0.1 &&
fabs(a.maxSpeed - b.maxSpeed) < 0.1 &&
a.restrPosRestr == b.restrPosRestr &&
a.restrNegRestr == b.restrNegRestr &&
a.noRestrFilter == b.noRestrFilter;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,6 @@
#include <string>
#include <unordered_map>
#include <vector>
#include "ad/cppgtfs/gtfs/Feed.h"
#include "ad/cppgtfs/gtfs/Route.h"
#include "pfaedle/gtfs/Feed.h"
@ -22,79 +21,132 @@ using ad::cppgtfs::gtfs::Stop;
namespace pfaedle {
namespace router {
extern double time;
struct NodeCand {
trgraph::Node* nd;
double pen;
};
struct EdgeCand {
trgraph::Edge* e;
double pen;
double progr;
POINT point;
int time;
std::vector<size_t> depPrede;
};
struct RoutingOpts {
RoutingOpts()
: fullTurnPunishFac(1000),
: fullTurnPunishFac(2000),
fullTurnAngle(45),
lineUnmatchedPunishFact(1),
lineNameFromUnmatchedPunishFact(1),
lineNameToUnmatchedPunishFact(1),
noLinesPunishFact(1),
passThruStationsPunish(100),
oneWayPunishFac(1),
oneWayEdgePunish(0),
lineUnmatchedPunishFact(0.5),
noLinesPunishFact(0),
platformUnmatchedPen(0),
stationDistPenFactor(0),
turnRestrCost(0),
popReachEdge(true),
noSelfHops(true) {}
uint32_t fullTurnPunishFac;
double fullTurnPunishFac;
double fullTurnAngle;
double passThruStationsPunish;
double oneWayPunishFac;
double oneWayEdgePunish;
double lineUnmatchedPunishFact;
double lineNameFromUnmatchedPunishFact;
double lineNameToUnmatchedPunishFact;
double noLinesPunishFact;
double platformUnmatchedPen;
double stationUnmatchedPen;
double stationDistPenFactor;
double nonStationPen;
uint32_t turnRestrCost;
double nonOsmPen;
double levelPunish[8];
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 a.fullTurnPunishFac == b.fullTurnPunishFac &&
return fabs(a.fullTurnPunishFac - b.fullTurnPunishFac) < 0.01 &&
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 &&
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;
fabs(a.nonOsmPen - b.nonOsmPen) < 0.01 &&
fabs(a.levelPunish[0] - b.levelPunish[0]) < 0.01 &&
fabs(a.levelPunish[1] - b.levelPunish[1]) < 0.01 &&
fabs(a.levelPunish[2] - b.levelPunish[2]) < 0.01 &&
fabs(a.levelPunish[3] - b.levelPunish[3]) < 0.01 &&
fabs(a.levelPunish[4] - b.levelPunish[4]) < 0.01 &&
fabs(a.levelPunish[5] - b.levelPunish[5]) < 0.01 &&
fabs(a.levelPunish[6] - b.levelPunish[6]) < 0.01 &&
fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01 &&
a.popReachEdge == b.popReachEdge && a.noSelfHops == b.noSelfHops;
}
struct EdgeCost {
EdgeCost() : _cost(0) {}
explicit EdgeCost(double cost) : _cost(cost) {}
EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3,
double mDistLvl4, double mDistLvl5, double mDistLvl6,
double mDistLvl7, uint32_t fullTurns, int32_t passThru,
double oneWayMeters, size_t oneWayEdges, double lineUnmatchedMeters,
double noLinesMeters, double reachPen, const RoutingOpts* o) {
if (!o) {
_cost = mDist + reachPen;
} else {
_cost = mDist * o->levelPunish[0] + mDistLvl1 * o->levelPunish[1] +
mDistLvl2 * o->levelPunish[2] + mDistLvl3 * o->levelPunish[3] +
mDistLvl4 * o->levelPunish[4] + mDistLvl5 * o->levelPunish[5] +
mDistLvl6 * o->levelPunish[6] + mDistLvl7 * o->levelPunish[7] +
oneWayMeters * o->oneWayPunishFac +
oneWayEdges * o->oneWayEdgePunish +
lineUnmatchedMeters * o->lineUnmatchedPunishFact +
noLinesMeters * o->noLinesPunishFact +
fullTurns * o->fullTurnPunishFac +
passThru * o->passThruStationsPunish + reachPen;
}
}
float _cost;
double getValue() const { return _cost; }
};
// _____________________________________________________________________________
inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) {
return EdgeCost(a.getValue() + b.getValue());
}
// _____________________________________________________________________________
inline bool operator<=(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() <= b.getValue();
}
// _____________________________________________________________________________
inline bool operator==(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() == b.getValue();
}
// _____________________________________________________________________________
inline bool operator>(const EdgeCost& a, const EdgeCost& b) {
return a.getValue() > b.getValue();
}
// _____________________________________________________________________________
template <typename F>
inline bool angSmaller(const Point<F>& f, const Point<F>& m, const Point<F>& t,
double ang) {
if (util::geo::innerProd(m, f, t) < ang) return 1;
return 0;
}
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;
@ -102,12 +154,8 @@ typedef std::vector<trgraph::Node*> NodeList;
struct EdgeListHop {
EdgeList edges;
const trgraph::Edge* start;
const trgraph::Edge* end;
double progrStart;
double progrEnd;
POINT pointStart;
POINT pointEnd;
const trgraph::Node* start;
const trgraph::Node* end;
};
typedef std::vector<EdgeListHop> EdgeListHops;
@ -149,27 +197,9 @@ inline pfaedle::router::FeedStops writeMotStops(const pfaedle::gtfs::Feed* feed,
// _____________________________________________________________________________
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) {
for (const auto& mot : mots) {
if (first) motStr += ", ";
motStr += "<" + ad::cppgtfs::gtfs::flat::Route::getTypeString(mot) + ">";
first = true;

View file

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

View file

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

View file

@ -7,95 +7,197 @@
#include <limits>
#include <map>
#include <mutex>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "pfaedle/Def.h"
#include "pfaedle/osm/Restrictor.h"
#include "pfaedle/router/HopCache.h"
#include "pfaedle/router/Graph.h"
#include "pfaedle/router/Misc.h"
#include "pfaedle/router/RoutingAttrs.h"
#include "pfaedle/router/TripTrie.h"
#include "pfaedle/router/Weights.h"
#include "pfaedle/trgraph/Graph.h"
#include "util/Misc.h"
#include "util/geo/Geo.h"
#include "util/graph/Dijkstra.h"
#include "util/graph/EDijkstra.h"
using util::graph::EDijkstra;
using util::graph::Dijkstra;
namespace pfaedle {
namespace router {
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::unordered_map<const trgraph::Edge*, router::Node*> CombNodeMap;
typedef std::pair<size_t, size_t> HId;
typedef std::vector<double> LayerCostsDAG;
typedef std::vector<LayerCostsDAG> CostsDAG;
typedef std::vector<std::vector<size_t>> PredeDAG;
typedef std::map<
RoutingAttrs,
std::unordered_map<const trgraph::Edge*,
std::unordered_map<const trgraph::Edge*,
std::pair<EdgeCost, EdgeList> > > >
Cache;
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 HopBand {
double minD;
double maxD;
const trgraph::Edge* nearest;
double maxInGrpDist;
};
typedef std::vector<std::pair<std::pair<size_t, size_t>, uint32_t>> CostMatrix;
struct CostFunc
: public EDijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp,
double max)
: _rAttrs(rAttrs),
_rOpts(rOpts),
_res(res),
_tgGrp(tgGrp),
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, max, 0) {}
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;
const RoutingAttrs& _rAttrs;
const RoutingOpts& _rOpts;
const osm::Restrictor& _res;
const trgraph::StatGroup* _tgGrp;
EdgeCost _inf;
EdgeCost operator()(const trgraph::Edge* from, const trgraph::Node* n,
const trgraph::Edge* to) const;
EdgeCost inf() const { return _inf; }
double transitLineCmp(const trgraph::EdgePL& e) const;
};
struct NCostFunc
: public Dijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
NCostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp)
: _rAttrs(rAttrs),
_rOpts(rOpts),
_res(res),
_tgGrp(tgGrp),
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
std::numeric_limits<double>::infinity(), 0) {}
const RoutingAttrs& _rAttrs;
const RoutingOpts& _rOpts;
const osm::Restrictor& _res;
const trgraph::StatGroup* _tgGrp;
EdgeCost _inf;
EdgeCost operator()(const trgraph::Node* from, const trgraph::Edge* e,
const trgraph::Node* to) const;
EdgeCost inf() const { return _inf; }
double transitLineCmp(const trgraph::EdgePL& e) const;
};
struct DistHeur
: public EDijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
const std::set<trgraph::Edge*>& tos);
const RoutingOpts& _rOpts;
uint8_t _lvl;
POINT _center;
double _maxCentD;
EdgeCost operator()(const trgraph::Edge* a,
const std::set<trgraph::Edge*>& b) const;
};
struct NDistHeur
: public Dijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
NDistHeur(const RoutingOpts& rOpts, const std::set<trgraph::Node*>& tos);
const RoutingOpts& _rOpts;
POINT _center;
double _maxCentD;
EdgeCost operator()(const trgraph::Node* a,
const std::set<trgraph::Node*>& b) const;
};
struct CombCostFunc
: public EDijkstra::CostFunc<router::NodePL, router::EdgePL, double> {
explicit CombCostFunc(const RoutingOpts& rOpts) : _rOpts(rOpts) {}
const RoutingOpts& _rOpts;
double operator()(const router::Edge* from, const router::Node* n,
const router::Edge* to) const;
double inf() const { return std::numeric_limits<double>::infinity(); }
};
/*
* Finds the most likely route of schedule-based vehicle between stops in a
* physical transportation network
*/
template <typename TW>
class RouterImpl : public Router {
class Router {
public:
// 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;
// Init this router with caches for numThreads threads
explicit Router(size_t numThreads, bool caching);
~Router();
// Find the most likely path through the graph for a node candidate route.
EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts, const osm::Restrictor& rest,
router::Graph* cgraph) const;
// Find the most likely path through the graph for an edge candidate route.
EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
EdgeListHops route(const EdgeCandRoute& route, const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts, const osm::Restrictor& rest,
router::Graph* cgraph) const;
// Find the most likely path through cgraph for a node candidate route, but
// based on a greedy node to node approach
EdgeListHops routeGreedy(const NodeCandRoute& route,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
// Find the most likely path through cgraph for a node candidate route, but
// based on a greedy node to node set approach
EdgeListHops routeGreedy2(const NodeCandRoute& route,
const RoutingAttrs& rAttrs,
const RoutingOpts& rOpts,
const osm::Restrictor& rest) const;
// Return the number of thread caches this router was initialized with
size_t getCacheNumber() const;
private:
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;
bool _caching;
HopBand getHopBand(const EdgeCandGroup& a, const EdgeCandGroup& 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;
uint32_t addNonOverflow(uint32_t a, uint32_t b) const;
bool compConned(const EdgeCandGroup& a, const EdgeCandGroup& b) const;
};
#include "pfaedle/router/Router.tpp"
} // namespace router
} // namespace pfaedle

View file

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

View file

@ -5,10 +5,8 @@
#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;
@ -16,81 +14,40 @@ 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()
: 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;
RoutingAttrs() : fromString(""), toString(""), shortName(""), _simiCache() {}
std::string fromString;
std::string toString;
std::string shortName;
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};
mutable std::map<const TransitEdgeLine*, double> _simiCache;
// carfull: lower return value = higher similarity
double simi(const TransitEdgeLine* line) const {
auto i = _simiCache.find(line);
if (i != _simiCache.end()) return i->second;
LineSimilarity ret{false, false, false};
double cur = 1;
if (shortName.empty() || router::lineSimi(line->shortName, shortName) > 0.5)
ret.nameSimilar = true;
cur -= 0.333333333;
if (lineTo.size() == 0) {
ret.toSimilar = true;
} else {
for (const auto& lTo : lineTo) {
if (lTo.empty() || classifier->similar(line->toStr, lTo)) {
ret.toSimilar = true;
break;
}
}
}
if (toString.empty() || line->toStr.empty() ||
router::statSimi(line->toStr, toString) > 0.5)
cur -= 0.333333333;
if (lineFrom.empty() || classifier->similar(line->fromStr, lineFrom))
ret.fromSimilar = true;
if (fromString.empty() || line->fromStr.empty() ||
router::statSimi(line->fromStr, fromString) > 0.5)
cur -= 0.333333333;
_simiCache[line] = ret;
_simiCache[line] = cur;
return ret;
}
void merge(const RoutingAttrs& other) {
assert(other.lineFrom == lineFrom);
assert(other.shortName == shortName);
for (const auto& l : other.lineTo) {
auto i = std::lower_bound(lineTo.begin(), lineTo.end(), l);
if (i != lineTo.end() && (*i) == l) continue; // already present
lineTo.insert(i, l);
}
return cur;
}
};
inline bool operator==(const RoutingAttrs& a, const RoutingAttrs& b) {
return a.shortName == b.shortName && a.lineFrom == b.lineFrom;
return a.shortName == b.shortName && a.toString == b.toString &&
a.fromString == b.fromString;
}
inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) {
@ -98,8 +55,10 @@ inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) {
}
inline bool operator<(const RoutingAttrs& a, const RoutingAttrs& b) {
return a.lineFrom < b.lineFrom ||
(a.lineFrom == b.lineFrom && a.shortName < b.shortName);
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);
}
} // namespace router

File diff suppressed because it is too large Load diff

View file

@ -5,46 +5,43 @@
#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 {
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*>>
using ad::cppgtfs::gtfs::Stop;
using pfaedle::gtfs::Trip;
using pfaedle::gtfs::Feed;
struct Shape {
router::EdgeListHops hops;
double avgHopDist;
};
typedef std::vector<Trip*> Cluster;
typedef std::vector<Cluster> Clusters;
typedef std::pair<const Stop*, const Stop*> StopPair;
typedef std::unordered_map<const Trip*, router::RoutingAttrs> TripRAttrs;
typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
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
@ -52,121 +49,76 @@ typedef std::unordered_map<const ad::cppgtfs::gtfs::Stop*, EdgeCandGroup>
*/
class ShapeBuilder {
public:
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);
ShapeBuilder(Feed* feed, ad::cppgtfs::gtfs::Feed* evalFeed, MOTs mots,
const config::MotConfig& motCfg, eval::Collector* ecoll,
trgraph::Graph* g, router::FeedStops* stops,
osm::Restrictor* restr, const config::Config& cfg);
Stats shapeify(pfaedle::netgraph::Graph* outNg);
void shape(pfaedle::netgraph::Graph* ng);
router::FeedStops* getFeedStops();
// shape single trip
std::pair<std::vector<LINE>, Stats> shapeL(pfaedle::gtfs::Trip* trip);
const NodeCandGroup& getNodeCands(const Stop* s) const;
std::map<size_t, EdgeListHops> shapeify(
const TripTrie<pfaedle::gtfs::Trip>* trie, HopCache* hopCache) const;
EdgeListHops shapeify(pfaedle::gtfs::Trip* trip);
LINE shapeL(const router::NodeCandRoute& ncr,
const router::RoutingAttrs& rAttrs);
LINE shapeL(Trip* trip);
pfaedle::router::Shape shape(Trip* trip) const;
pfaedle::router::Shape shape(Trip* trip);
const trgraph::Graph* getGraph() const;
static void getGtfsBox(const pfaedle::gtfs::Feed* feed, const MOTs& mots,
static void getGtfsBox(const Feed* feed, const MOTs& mots,
const std::string& tid, bool dropShapes,
osm::BBoxIdx* box, double maxSpeed,
std::vector<double>* hopDists, uint8_t verbosity);
osm::BBoxIdx* box);
private:
pfaedle::gtfs::Feed* _feed;
Feed* _feed;
ad::cppgtfs::gtfs::Feed* _evalFeed;
MOTs _mots;
config::MotConfig _motCfg;
eval::Collector* _ecoll;
config::Config _cfg;
trgraph::Graph* _g;
router::Router _crouter;
router::FeedStops* _stops;
EdgeCandGroup _emptyNCG;
NodeCandGroup _emptyNCG;
size_t _curShpCnt;
size_t _curShpCnt, _numThreads;
std::mutex _shpMutex;
TripRAttrs _rAttrs;
osm::Restrictor* _restr;
const pfaedle::statsimiclassifier::StatsimiClassifier* _classifier;
GrpCache _grpCache;
router::Router* _router;
void buildGraph(router::FeedStops* fStops);
TripForests clusterTrips(pfaedle::gtfs::Feed* f, MOTs mots);
void buildNetGraph(TrGraphEdgs* edgs, pfaedle::netgraph::Graph* ng) const;
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;
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);
std::string getFreeShapeId(Trip* t);
void setShape(pfaedle::gtfs::Trip* t, const ad::cppgtfs::gtfs::Shape& s,
const std::vector<float>& dists);
ad::cppgtfs::gtfs::Shape getGtfsShape(const Shape& shp, Trip* t,
std::vector<double>* hopDists);
EdgeCandGroup getEdgCands(const ad::cppgtfs::gtfs::Stop* s) const;
void setShape(Trip* t, const ad::cppgtfs::gtfs::Shape& s,
const std::vector<double>& dists);
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;
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;
};
} // namespace router
} // namespace pfaedle

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,19 +3,22 @@
// 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::Component;
using pfaedle::trgraph::NodePL;
using pfaedle::trgraph::StatInfo;
using pfaedle::trgraph::NodePL;
using pfaedle::trgraph::Component;
std::vector<Component> NodePL::comps;
std::vector<StatInfo> NodePL::_statInfos;
// 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;
// _____________________________________________________________________________
NodePL::NodePL()
@ -29,6 +32,19 @@ NodePL::NodePL()
{
}
// _____________________________________________________________________________
NodePL::NodePL(const NodePL& pl)
: _geom(pl._geom),
_si(0),
_component(pl._component)
#ifdef PFAEDLE_DBG
,
_vis(pl._vis)
#endif
{
if (pl._si) setSI(*(pl._si));
}
// _____________________________________________________________________________
NodePL::NodePL(const POINT& geom)
: _geom(geom),
@ -54,6 +70,18 @@ NodePL::NodePL(const POINT& geom, const StatInfo& si)
setSI(si);
}
// _____________________________________________________________________________
NodePL::~NodePL() {
if (getSI()) delete _si;
if (_component) {
_comps[_component]--;
if (_comps[_component] == 0) {
delete _component;
_comps.erase(_comps.find(_component));
}
}
}
// _____________________________________________________________________________
void NodePL::setVisited() const {
#ifdef PFAEDLE_DBG
@ -65,14 +93,18 @@ void NodePL::setVisited() const {
void NodePL::setNoStat() { _si = 0; }
// _____________________________________________________________________________
const Component& NodePL::getComp() const { return comps[_component - 1]; }
const Component* NodePL::getComp() const { return _component; }
// _____________________________________________________________________________
uint32_t NodePL::getCompId() const { return _component; }
void NodePL::setComp(const Component* c) {
if (_component == c) return;
_component = c;
// _____________________________________________________________________________
void NodePL::setComp(uint32_t id) {
_component = id;
// NOT thread safe!
if (!_comps.count(c))
_comps[c] = 1;
else
_comps[c]++;
}
// _____________________________________________________________________________
@ -84,59 +116,54 @@ void NodePL::setGeom(const POINT& geom) { _geom = geom; }
// _____________________________________________________________________________
util::json::Dict NodePL::getAttrs() const {
util::json::Dict obj;
obj["component"] = std::to_string(_component);
obj["component"] = std::to_string(reinterpret_cast<size_t>(_component));
#ifdef PFAEDLE_DBG
obj["dijkstra_vis"] = _vis ? "yes" : "no";
#endif
if (getSI()) {
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();
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()));
#ifdef PFAEDLE_STATION_IDS
// only print this in debug mode
obj["station_id"] = getSI()->getId();
obj["station_id"] = _si->getId();
#endif
std::stringstream gtfsIds;
if (_si->getGroup()) {
for (auto* s : _si->getGroup()->getStops()) {
gtfsIds << s->getId() << " (" << s->getName() << "),";
}
}
obj["station_group_stops"] = gtfsIds.str();
}
return obj;
}
// _____________________________________________________________________________
void NodePL::setSI(const StatInfo& si) {
_statInfos.emplace_back(si);
_si = _statInfos.size();
}
void NodePL::setSI(const StatInfo& si) { _si = new StatInfo(si); }
// _____________________________________________________________________________
const StatInfo* NodePL::getSI() const {
if (isBlocker()) return 0;
if (isTurnCycle()) return 0;
if (_si == 0) return 0;
return &_statInfos[_si - 1];
return _si;
}
// _____________________________________________________________________________
StatInfo* NodePL::getSI() {
if (isBlocker()) return 0;
if (isTurnCycle()) return 0;
if (_si == 0) return 0;
return &_statInfos[_si - 1];
return _si;
}
// _____________________________________________________________________________
void NodePL::setTurnCycle() { _si = std::numeric_limits<uint32_t>::max() - 1; }
void NodePL::setBlocker() { _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();
}
bool NodePL::isBlocker() const { return _si == &_blockerSI; }

View file

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

View file

@ -5,6 +5,7 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include <mutex>
#include <regex>
#include <sstream>
#include <stdexcept>
@ -36,6 +37,17 @@ Normalizer& Normalizer::operator=(Normalizer other) {
return *this;
}
// _____________________________________________________________________________
std::string Normalizer::operator()(std::string sn) const {
return normTS(sn);
}
// _____________________________________________________________________________
std::string Normalizer::normTS(const std::string& sn) const {
std::lock_guard<std::mutex> lock(_mutex);
return norm(sn);
}
// _____________________________________________________________________________
std::string Normalizer::norm(const std::string& sn) const {
auto i = _cache.find(sn);

View file

@ -10,6 +10,7 @@
#include <unordered_map>
#include <utility>
#include <vector>
#include <mutex>
namespace pfaedle {
namespace trgraph {
@ -36,13 +37,19 @@ class Normalizer {
// Normalize sn, not thread safe
std::string norm(const std::string& sn) const;
// Normalize sn, thread safe
std::string normTS(const std::string& sn) const;
// Normalize sn based on the rules of this normalizer, uses the thread safe
// version of norm() internally
std::string operator()(std::string sn) const;
bool operator==(const Normalizer& b) const;
private:
ReplRulesComp _rules;
ReplRules _rulesOrig;
mutable std::unordered_map<std::string, std::string> _cache;
mutable std::mutex _mutex;
void buildRules(const ReplRules& rules);
};

View file

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

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

View file

@ -3,24 +3,66 @@
// 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("") {}
StatInfo::StatInfo() : _name(""), _track(""), _fromOsm(false), _group(0) {}
// _____________________________________________________________________________
StatInfo::StatInfo(const StatInfo& si)
: _name(si._name), _altNames(si._altNames), _track(si._track) {
: _name(si._name),
_altNames(si._altNames),
_track(si._track),
_fromOsm(si._fromOsm),
_group(0) {
setGroup(si._group);
#ifdef PFAEDLE_STATION_IDS
_id = si._id;
#endif
}
// _____________________________________________________________________________
StatInfo::StatInfo(const std::string& name, const std::string& track)
: _name(name), _track(track) {}
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; }
// _____________________________________________________________________________
const std::string& StatInfo::getName() const { return _name; }
@ -28,6 +70,12 @@ const std::string& StatInfo::getName() const { return _name; }
// _____________________________________________________________________________
const std::string& StatInfo::getTrack() const { return _track; }
// _____________________________________________________________________________
bool StatInfo::isFromOsm() const { return _fromOsm; }
// _____________________________________________________________________________
void StatInfo::setIsFromOsm(bool is) { _fromOsm = is; }
// _____________________________________________________________________________
double StatInfo::simi(const StatInfo* other) const {
if (!other) return 0;

View file

@ -6,20 +6,24 @@
#define PFAEDLE_TRGRAPH_STATINFO_H_
#include <string>
#include <unordered_map>
#include <vector>
#include <unordered_map>
namespace pfaedle {
namespace trgraph {
// forward declaration
class StatGroup;
/*
* Meta information (name, alternative names, track, ...) of a single stop
* Meta information (name, alternative names, track, group...) of a single stop
*/
class StatInfo {
public:
StatInfo();
StatInfo(const StatInfo& si);
StatInfo(const std::string& name, const std::string& track);
StatInfo(const std::string& name, const std::string& track, bool _fromOsm);
~StatInfo();
// Return this stops names.
const std::string& getName() const;
@ -39,6 +43,18 @@ 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; }
@ -48,12 +64,17 @@ class StatInfo {
std::string _name;
std::vector<std::string> _altNames;
std::string _track;
bool _fromOsm;
StatGroup* _group;
#ifdef PFAEDLE_STATION_IDS
// debug feature to store station ids from both OSM
// and GTFS
std::string _id;
#endif
static std::unordered_map<const StatGroup*, size_t> _groups;
static void unRefGroup(StatGroup* g);
};
} // namespace trgraph
} // namespace pfaedle

View file

@ -1,17 +0,0 @@
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})

View file

@ -1,485 +0,0 @@
// 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;
}

View file

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

View file

@ -1,276 +0,0 @@
// 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 +0,0 @@
Subproject commit d1c30e9ec4cb68803be073d35beb6af2b860bda4

12
src/util/CMakeLists.txt Normal file
View file

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

136
src/util/Misc.h Normal file
View file

@ -0,0 +1,136 @@
// Copyright 2017, University of Freiburg,
// Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifndef UTIL_MISC_H_
#define UTIL_MISC_H_
#include <cmath>
#include <cstring>
#include <chrono>
#include <sstream>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#define UNUSED(expr) do { (void)(expr); } while (0)
#define TIME() std::chrono::high_resolution_clock::now()
#define TOOK(t1, t2) (std::chrono::duration_cast<microseconds>(t2 - t1).count() / 1000.0)
#define T_START(n) auto _tstart_##n = std::chrono::high_resolution_clock::now()
#define T_STOP(n) (std::chrono::duration_cast<microseconds>(std::chrono::high_resolution_clock::now() - _tstart_##n).count() / 1000.0)
namespace util {
// cached first 10 powers of 10
static int pow10[10] = {
1, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000};
// _____________________________________________________________________________
inline uint64_t factorial(uint64_t n) {
if (n == 1) return n;
return n * factorial(n - 1);
}
// _____________________________________________________________________________
inline uint64_t atoul(const char* p) {
uint64_t ret = 0;
while (*p) {
ret = ret * 10 + (*p++ - '0');
}
return ret;
}
// _____________________________________________________________________________
inline bool isFloatingPoint(const std::string& str) {
std::stringstream ss(str);
double f;
ss >> std::noskipws >> f;
return ss.eof() && ! ss.fail();
}
// _____________________________________________________________________________
inline double atof(const char* p, uint8_t mn) {
// this atof implementation works only on "normal" float strings like
// 56.445 or -345.00, but should be faster than std::atof
double ret = 0.0;
bool neg = false;
if (*p == '-') {
neg = true;
p++;
}
while (*p >= '0' && *p <= '9') {
ret = ret * 10.0 + (*p - '0');
p++;
}
if (*p == '.') {
p++;
double f = 0;
uint8_t n = 0;
for (; n < mn && *p >= '0' && *p <= '9'; n++, p++) {
f = f * 10.0 + (*p - '0');
}
if (n < 10)
ret += f / pow10[n];
else
ret += f / std::pow(10, n);
}
if (neg) return -ret;
return ret;
}
// _____________________________________________________________________________
inline double atof(const char* p) { return atof(p, 38); }
// _____________________________________________________________________________
inline std::string getHomeDir() {
// parse implicit paths
const char* homedir = 0;
char* buf = 0;
if ((homedir = getenv("HOME")) == 0) {
homedir = "";
struct passwd pwd;
struct passwd* result;
size_t bufsize;
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == static_cast<size_t>(-1)) bufsize = 0x4000;
buf = static_cast<char*>(malloc(bufsize));
if (buf != 0) {
getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
if (result != NULL) homedir = result->pw_dir;
}
}
std::string ret(homedir);
if (buf) free(buf);
return ret;
}
// _____________________________________________________________________________
inline std::string getTmpDir() {
// first, check if an env variable is set
const char* tmpdir = getenv("TMPDIR");
if (tmpdir && std::strlen(tmpdir)) return std::string(tmpdir);
// second, check if /tmp is writable
if (access("/tmp/", W_OK) == 0) return "/tmp";
// third, check if the cwd is writable
if (access(".", W_OK) == 0) return ".";
// lastly, return the users home directory as a fallback
return getHomeDir();
}
} // namespace util
#endif // UTIL_MISC_H_

116
src/util/Nullable.h Normal file
View file

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

260
src/util/String.h Normal file
View file

@ -0,0 +1,260 @@
// 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 <iomanip>
#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& unesc) {
// modified code from
// http://stackoverflow.com/questions/7724448/simple-json-string-escape-for-c
std::ostringstream o;
for (auto c = unesc.cbegin(); c != unesc.cend(); c++) {
switch (*c) {
case '"':
o << "\\\"";
break;
case '\\':
o << "\\\\";
break;
case '\b':
o << "\\b";
break;
case '\f':
o << "\\f";
break;
case '\n':
o << "\\n";
break;
case '\r':
o << "\\r";
break;
case '\t':
o << "\\t";
break;
default:
if ('\x00' <= *c && *c <= '\x1f') {
o << "\\u" << std::hex << std::setw(4) << std::setfill('0')
<< static_cast<int>(*c);
} else {
o << *c;
}
}
}
return o.str();
}
// _____________________________________________________________________________
inline bool replace(std::string& subj, const std::string& from,
const std::string& to) {
if (from.empty()) return false;
size_t start_pos = subj.find(from);
if (start_pos != std::string::npos) {
subj.replace(start_pos, from.length(), to);
return true;
}
return false;
}
// _____________________________________________________________________________
inline bool replaceAll(std::string& subj, const std::string& from,
const std::string& to) {
if (from.empty()) return false;
bool found = false;
size_t s = subj.find(from, 0);
for (; s != std::string::npos; s = subj.find(from, s + to.length())) {
found = true;
subj.replace(s, from.length(), to);
}
return found;
}
// _____________________________________________________________________________
inline std::string unixBasename(const std::string& pathname) {
return {std::find_if(pathname.rbegin(), pathname.rend(),
[](char c) { return c == '/'; })
.base(),
pathname.end()};
}
// _____________________________________________________________________________
template <typename T>
inline std::string toString(T obj) {
std::stringstream ss;
ss << obj;
return ss.str();
}
// _____________________________________________________________________________
inline std::vector<std::string> split(std::string in, char sep) {
std::stringstream ss(in);
std::vector<std::string> ret(1);
while (std::getline(ss, ret.back(), sep)) {
ret.push_back("");
}
ret.pop_back();
return ret;
}
// _____________________________________________________________________________
inline std::string ltrim(std::string str) {
str.erase(0, str.find_first_not_of(" \t\n\v\f\r"));
return str;
}
// _____________________________________________________________________________
inline std::string rtrim(std::string str) {
str.erase(str.find_last_not_of(" \t\n\v\f\r") + 1);
return str;
}
// _____________________________________________________________________________
inline std::string trim(std::string str) { return ltrim(rtrim(str)); }
// _____________________________________________________________________________
inline size_t editDist(const std::string& s1, const std::string& s2) {
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
size_t len1 = s1.size();
size_t len2 = s2.size();
std::vector<size_t> cur(len2 + 1);
std::vector<size_t> prev(len2 + 1);
for (size_t i = 0; i < prev.size(); i++) prev[i] = i;
for (size_t i = 0; i < len1; i++) {
cur[0] = i + 1;
for (size_t j = 0; j < len2; j++) {
cur[j + 1] =
std::min(prev[1 + j] + 1,
std::min(cur[j] + 1, prev[j] + (s1[i] == s2[j] ? 0 : 1)));
}
std::swap(cur, prev);
}
return prev[len2];
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const std::string& s,
size_t deltaMax) {
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
size_t len1 = prefix.size();
size_t len2 = std::min(s.size(), prefix.size() + deltaMax + 1);
std::vector<size_t> d((len1 + 1) * (len2 + 1));
d[0] = 0;
for (size_t i = 1; i <= len1; ++i) d[i * (len2 + 1)] = i;
for (size_t i = 1; i <= len2; ++i) d[i] = i;
for (size_t i = 1; i <= len1; i++) {
for (size_t j = 1; j <= len2; j++) {
d[i * (len2 + 1) + j] = std::min(std::min(d[(i - 1) * (len2 + 1) + j] + 1,
d[i * (len2 + 1) + j - 1] + 1),
d[(i - 1) * (len2 + 1) + j - 1] +
(prefix[i - 1] == s[j - 1] ? 0 : 1));
}
}
// take min of last row
size_t deltaMin = std::max(std::max(deltaMax + 1, prefix.size()), s.size());
for (size_t i = 0; i <= len2; i++) {
if (d[len1 * (len2 + 1) + i] < deltaMin)
deltaMin = d[len1 * (len2 + 1) + i];
}
return deltaMin;
}
// _____________________________________________________________________________
inline size_t prefixEditDist(const std::string& prefix, const std::string& s) {
return prefixEditDist(prefix, s, s.size());
}
// _____________________________________________________________________________
inline std::string toUpper(std::string str) {
std::transform(str.begin(), str.end(), str.begin(), toupper);
return str;
}
// _____________________________________________________________________________
inline std::string toLower(std::string str) {
std::transform(str.begin(), str.end(), str.begin(), tolower);
return str;
}
// _____________________________________________________________________________
template <class Iter>
inline std::string implode(Iter begin, const Iter& end, const char* del) {
std::stringstream ss;
size_t i = 0;
while (begin != end) {
if (i != 0) ss << del;
ss << *begin;
begin++;
i++;
}
return ss.str();
}
// _____________________________________________________________________________
inline std::string normalizeWhiteSpace(const std::string& input) {
std::string ret;
bool ws = false;
for (size_t i = 0; i < input.size(); i++) {
if (std::isspace(input[i])) {
if (!ws) {
ret += " ";
ws = true;
}
continue;
} else {
ws = false;
ret += input[i];
}
}
return ret;
}
// _____________________________________________________________________________
template <typename T>
inline std::string implode(const std::vector<T>& vec, const char* del) {
return implode(vec.begin(), vec.end(), del);
}
}
#endif // UTIL_STRING_H_

View file

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

View file

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

91
src/util/geo/Box.h Normal file
View file

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

1630
src/util/geo/Geo.h Normal file

File diff suppressed because it is too large Load diff

32
src/util/geo/GeoGraph.h Normal file
View file

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

89
src/util/geo/Grid.h Normal file
View file

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

228
src/util/geo/Grid.tpp Normal file
View file

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

28
src/util/geo/Line.h Normal file
View file

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

50
src/util/geo/Point.h Normal file
View file

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

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