initial commit
This commit is contained in:
commit
efcd3e1892
106 changed files with 27000 additions and 0 deletions
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
Makefile
|
||||
CMakeCache.txt
|
||||
*.o
|
||||
*.a
|
||||
cmake_install.cmake
|
||||
*~
|
||||
CMakeFiles
|
||||
*.cfg
|
||||
*.cmake
|
||||
/build/
|
||||
*.pyc
|
||||
compile_commands.json
|
||||
.vimrc
|
||||
.clang_complete
|
||||
/.idea
|
||||
*.vs
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.cppr
|
||||
*.hr
|
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
[submodule "src/cppgtfs"]
|
||||
path = src/cppgtfs
|
||||
url = https://ad-git.informatik.uni-freiburg.de/ad/cppgtfs.git
|
||||
[submodule "src/xml"]
|
||||
path = src/xml
|
||||
url = https://git.patrickbrosi.de/patrick/xmlparser
|
||||
[submodule "src/configparser"]
|
||||
path = src/configparser
|
||||
url = https://git.patrickbrosi.de/patrick/configparser
|
195
.ycm_extra_conf.py
Normal file
195
.ycm_extra_conf.py
Normal file
|
@ -0,0 +1,195 @@
|
|||
# This file is NOT licensed under the GPLv3, which is the license for the rest
|
||||
# of YouCompleteMe.
|
||||
#
|
||||
# Here's the license text for this file:
|
||||
#
|
||||
# This is free and unencumbered software released into the public domain.
|
||||
#
|
||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
# distribute this software, either in source code form or as a compiled
|
||||
# binary, for any purpose, commercial or non-commercial, and by any
|
||||
# means.
|
||||
#
|
||||
# In jurisdictions that recognize copyright laws, the author or authors
|
||||
# of this software dedicate any and all copyright interest in the
|
||||
# software to the public domain. We make this dedication for the benefit
|
||||
# of the public at large and to the detriment of our heirs and
|
||||
# successors. We intend this dedication to be an overt act of
|
||||
# relinquishment in perpetuity of all present and future rights to this
|
||||
# software under copyright law.
|
||||
#
|
||||
# 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 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.
|
||||
#
|
||||
# For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
import os
|
||||
import ycm_core
|
||||
|
||||
# These are the compilation flags that will be used in case there's no
|
||||
# compilation database set (by default, one is not set).
|
||||
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
|
||||
flags = [
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-Werror',
|
||||
'-Wno-c++98-compat',
|
||||
'-Wno-long-long',
|
||||
'-Wno-variadic-macros',
|
||||
'-fexceptions',
|
||||
'-DNDEBUG',
|
||||
# You 100% do NOT need -DUSE_CLANG_COMPLETER in your flags; only the YCM
|
||||
# source code needs it.
|
||||
'-DUSE_CLANG_COMPLETER',
|
||||
# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
|
||||
# language to use when compiling headers. So it will guess. Badly. So C++
|
||||
# headers will be compiled as C headers. You don't want that so ALWAYS specify
|
||||
# a "-std=<something>".
|
||||
# For a C project, you would set this to something like 'c99' instead of
|
||||
# 'c++11'.
|
||||
'-std=c++11',
|
||||
# ...and the same thing goes for the magic -x option which specifies the
|
||||
# language that the files to be compiled are written in. This is mostly
|
||||
# relevant for c++ headers.
|
||||
# For a C project, you would set this to 'c' instead of 'c++'.
|
||||
'-x',
|
||||
'c++',
|
||||
'-isystem',
|
||||
'../BoostParts',
|
||||
'-isystem',
|
||||
# This path will only work on OS X, but extra paths that don't exist are not
|
||||
# harmful
|
||||
'/System/Library/Frameworks/Python.framework/Headers',
|
||||
'-isystem',
|
||||
'../llvm/include',
|
||||
'-isystem',
|
||||
'../llvm/tools/clang/include',
|
||||
'-I',
|
||||
'.',
|
||||
'-I',
|
||||
'./ClangCompleter',
|
||||
'-isystem',
|
||||
'./tests/gmock/gtest',
|
||||
'-isystem',
|
||||
'./tests/gmock/gtest/include',
|
||||
'-isystem',
|
||||
'./tests/gmock',
|
||||
'-isystem',
|
||||
'./tests/gmock/include'
|
||||
'-I',
|
||||
'/usr/lib/gcc/x86_64-linux-gnu/4.8/include/',
|
||||
'-I',
|
||||
'/usr/lib/gcc/x86_64-linux-gnu/5/include/',
|
||||
'-I',
|
||||
'/usr/lib/gcc/x86_64-linux-gnu/6/include/',
|
||||
'-fopenmp'
|
||||
]
|
||||
|
||||
|
||||
# Set this to the absolute path to the folder (NOT the file!) containing the
|
||||
# compile_commands.json file to use that instead of 'flags'. See here for
|
||||
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
|
||||
#
|
||||
# You can get CMake to generate this file for you by adding:
|
||||
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
|
||||
# to your CMakeLists.txt file.
|
||||
#
|
||||
# Most projects will NOT need to set this to anything; you can just change the
|
||||
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
|
||||
compilation_database_folder = 'build'
|
||||
|
||||
if os.path.exists( compilation_database_folder ):
|
||||
database = ycm_core.CompilationDatabase( compilation_database_folder )
|
||||
else:
|
||||
database = None
|
||||
|
||||
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
|
||||
|
||||
def DirectoryOfThisScript():
|
||||
return os.path.dirname( os.path.abspath( __file__ ) )
|
||||
|
||||
|
||||
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
|
||||
if not working_directory:
|
||||
return list( flags )
|
||||
new_flags = []
|
||||
make_next_absolute = False
|
||||
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
|
||||
for flag in flags:
|
||||
new_flag = flag
|
||||
|
||||
if make_next_absolute:
|
||||
make_next_absolute = False
|
||||
if not flag.startswith( '/' ):
|
||||
new_flag = os.path.join( working_directory, flag )
|
||||
|
||||
for path_flag in path_flags:
|
||||
if flag == path_flag:
|
||||
make_next_absolute = True
|
||||
break
|
||||
|
||||
if flag.startswith( path_flag ):
|
||||
path = flag[ len( path_flag ): ]
|
||||
new_flag = path_flag + os.path.join( working_directory, path )
|
||||
break
|
||||
|
||||
if new_flag:
|
||||
new_flags.append( new_flag )
|
||||
return new_flags
|
||||
|
||||
|
||||
def IsHeaderFile( filename ):
|
||||
extension = os.path.splitext( filename )[ 1 ]
|
||||
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
|
||||
|
||||
|
||||
def GetCompilationInfoForFile( filename ):
|
||||
# The compilation_commands.json file generated by CMake does not have entries
|
||||
# for header files. So we do our best by asking the db for flags for a
|
||||
# corresponding source file, if any. If one exists, the flags for that file
|
||||
# should be good enough.
|
||||
if IsHeaderFile( filename ):
|
||||
basename = os.path.splitext( filename )[ 0 ]
|
||||
for extension in SOURCE_EXTENSIONS:
|
||||
replacement_file = basename + extension
|
||||
if os.path.exists( replacement_file ):
|
||||
compilation_info = database.GetCompilationInfoForFile(
|
||||
replacement_file )
|
||||
if compilation_info.compiler_flags_:
|
||||
return compilation_info
|
||||
return None
|
||||
return database.GetCompilationInfoForFile( filename )
|
||||
|
||||
|
||||
def FlagsForFile( filename, **kwargs ):
|
||||
if database:
|
||||
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
|
||||
# python list, but a "list-like" StringVec object
|
||||
compilation_info = GetCompilationInfoForFile( filename )
|
||||
if not compilation_info:
|
||||
return None
|
||||
|
||||
final_flags = MakeRelativePathsInFlagsAbsolute(
|
||||
compilation_info.compiler_flags_,
|
||||
compilation_info.compiler_working_dir_ )
|
||||
|
||||
# NOTE: This is just for YouCompleteMe; it's highly likely that your project
|
||||
# does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR
|
||||
# ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT.
|
||||
try:
|
||||
final_flags.remove( '-stdlib=libc++' )
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
relative_to = DirectoryOfThisScript()
|
||||
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
|
||||
|
||||
return {
|
||||
'flags': final_flags,
|
||||
'do_cache': True
|
||||
}
|
86
CMakeLists.txt
Normal file
86
CMakeLists.txt
Normal file
|
@ -0,0 +1,86 @@
|
|||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
project (pfaedle)
|
||||
|
||||
set(CPPLINT "${CMAKE_SOURCE_DIR}/cpplint.py")
|
||||
include(cmake/cpplint.cmake)
|
||||
|
||||
set(CPPLINT_PROJECT_ROOT "src")
|
||||
|
||||
enable_testing()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
|
||||
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build")
|
||||
|
||||
|
||||
find_package(Boost COMPONENTS system filesystem REQUIRED)
|
||||
include_directories("build" ${Boost_INCLUDE_DIRS})
|
||||
|
||||
find_package(OpenMP)
|
||||
if(OPENMP_FOUND)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
# set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake
|
||||
set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -frename-registers -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -I/usr/lib/gcc/x86_64-linux-gnu/4.8/include/ -I/usr/lib/gcc/x86_64-linux-gnu/5/include/")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DLOGLEVEL=3")
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -g -DLOGLEVEL=3")
|
||||
|
||||
# export compile commands to tools like clang
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# Compiler-specific C++11 activation.
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
|
||||
if ((GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7))
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif (GCC_VERSION VERSION_EQUAL 4.6)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
else ()
|
||||
message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.6 or greater.")
|
||||
endif ()
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
else ()
|
||||
message(FATAL_ERROR "Your C++ compiler does not support C++11.")
|
||||
endif ()
|
||||
|
||||
# http://brianmilco.blogspot.de/2012/11/cmake-automatically-use-git-tags-as.html
|
||||
include(GetGitRevisionDescription)
|
||||
git_get_tag(VERSION_GIT)
|
||||
get_git_is_dirty(VERSION_GIT_IS_DIRTY)
|
||||
if ("${VERSION_GIT_IS_DIRTY}" STREQUAL "")
|
||||
set(VERSION_GIT_FULL "${VERSION_GIT}")
|
||||
else()
|
||||
set(VERSION_GIT_FULL "${VERSION_GIT}-${VERSION_GIT_IS_DIRTY}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
# tests
|
||||
|
||||
add_test("Utility Tests" utilTest)
|
||||
|
||||
# custom eval target
|
||||
|
||||
add_custom_target(
|
||||
eval
|
||||
COMMAND make
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}//eval
|
||||
)
|
||||
|
||||
# handles install target
|
||||
|
||||
install(
|
||||
FILES README.md pfaedle.cfg DESTINATION share/${PROJECT_NAME} PERMISSIONS WORLD_READ
|
||||
)
|
||||
|
||||
install(
|
||||
FILES build/pfaedle DESTINATION bin
|
||||
PERMISSIONS WORLD_EXECUTE
|
||||
)
|
151
cmake/GetGitRevisionDescription.cmake
Normal file
151
cmake/GetGitRevisionDescription.cmake
Normal file
|
@ -0,0 +1,151 @@
|
|||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the refspec and sha hash of the current head revision
|
||||
#
|
||||
# git_describe(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the source tree, and adjusting
|
||||
# the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe --exact-match on the source tree,
|
||||
# and adjusting the output so that it tests false if there was no exact
|
||||
# matching tag.
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}")
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
|
||||
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
|
||||
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
|
||||
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
endwhile()
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if(NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${GIT_DIR}/HEAD")
|
||||
return()
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
||||
"${GIT_DATA}/grabRef.cmake"
|
||||
@ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_is_dirty _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
diff-index --name-only HEAD --
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
if (NOT "${out}" STREQUAL "")
|
||||
set(IS_DIRTY "dirty")
|
||||
else()
|
||||
set(IS_DIRTY "")
|
||||
endif()
|
||||
set(${_var} "${IS_DIRTY}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO sanitize
|
||||
#if((${ARGN}" MATCHES "&&") OR
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
#endif()
|
||||
|
||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
describe
|
||||
${hash}
|
||||
${ARGN}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_tag _var)
|
||||
git_describe(out --tags ${ARGN})
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
endfunction()
|
38
cmake/GetGitRevisionDescription.cmake.in
Normal file
38
cmake/GetGitRevisionDescription.cmake.in
Normal file
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
set(HEAD_HASH "${HEAD_REF}")
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
12
cmake/MacPlistMacros.cmake
Normal file
12
cmake/MacPlistMacros.cmake
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Mac Plist Macros
|
||||
|
||||
FUNCTION (GET_VERSION_PLIST PLISTFILE OUTVAR)
|
||||
SET (PVERSION "")
|
||||
IF (EXISTS ${PLISTFILE})
|
||||
FILE (READ "${PLISTFILE}" info_plist)
|
||||
STRING (REGEX REPLACE "\n" "" info_plist "${info_plist}")
|
||||
STRING (REGEX MATCH "<key>CFBundleShortVersionString</key>[ \t]*<string>([0-9\\.]*)</string>" PLISTVERSION "${info_plist}")
|
||||
STRING (REGEX REPLACE "<key>CFBundleShortVersionString</key>[ \t]*<string>([0-9\\.]*)</string>" "\\1" PVERSION "${PLISTVERSION}")
|
||||
ENDIF (EXISTS ${PLISTFILE})
|
||||
SET (${OUTVAR} ${PVERSION} PARENT_SCOPE)
|
||||
ENDFUNCTION (GET_VERSION_PLIST)
|
133
cmake/cpplint.cmake
Normal file
133
cmake/cpplint.cmake
Normal 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
6233
cpplint.py
vendored
Executable file
File diff suppressed because it is too large
Load diff
4693
geo/pfaedle.qgs
Normal file
4693
geo/pfaedle.qgs
Normal file
File diff suppressed because it is too large
Load diff
13
src/CMakeLists.txt
Normal file
13
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
set(PFAEDLE_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
cpplint_add_subdirectory(pfaedle)
|
||||
|
||||
include_directories(
|
||||
${PFAEDLE_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(pfaedle)
|
||||
add_subdirectory(cppgtfs)
|
||||
add_subdirectory(xml)
|
||||
add_subdirectory(configparser)
|
20
src/pfaedle/CMakeLists.txt
Normal file
20
src/pfaedle/CMakeLists.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
file(GLOB_RECURSE pfaedle_SRC *.cpp)
|
||||
|
||||
set(pfaedle_main PfaedleMain.cpp)
|
||||
|
||||
list(REMOVE_ITEM pfaedle_SRC ${pfaedle_main})
|
||||
|
||||
include_directories(
|
||||
${PFAEDLE_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
configure_file (
|
||||
"_config.h.in"
|
||||
"_config.h"
|
||||
)
|
||||
|
||||
add_executable(pfaedle ${pfaedle_main})
|
||||
add_library(pfaedle_dep ${pfaedle_SRC})
|
||||
|
||||
target_include_directories(pfaedle_dep PUBLIC ${PROJECT_SOURCE_DIR}/src/cppgtfs/src)
|
||||
target_link_libraries(pfaedle pfaedle_dep util xml configparser ad_cppgtfs ${Boost_LIBRARIES} -lpthread)
|
188
src/pfaedle/PfaedleMain.cpp
Normal file
188
src/pfaedle/PfaedleMain.cpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/Parser.h"
|
||||
#include "ad/cppgtfs/Writer.h"
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/config/ConfigReader.h"
|
||||
#include "pfaedle/config/MotConfig.h"
|
||||
#include "pfaedle/config/MotConfigReader.h"
|
||||
#include "pfaedle/eval/Collector.h"
|
||||
#include "pfaedle/netgraph/Graph.h"
|
||||
#include "pfaedle/osm/OsmIdSet.h"
|
||||
#include "pfaedle/router/ShapeBuilder.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "util/geo/output/GeoGraphJsonOutput.h"
|
||||
#include "util/geo/output/GeoJsonOutput.h"
|
||||
#include "util/json/JsonWriter.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
using std::string;
|
||||
using pfaedle::router::MOTs;
|
||||
using pfaedle::osm::BBoxIdx;
|
||||
using pfaedle::osm::OsmBuilder;
|
||||
using pfaedle::config::MotConfig;
|
||||
using pfaedle::config::Config;
|
||||
using pfaedle::router::ShapeBuilder;
|
||||
using pfaedle::config::MotConfigReader;
|
||||
using pfaedle::config::ConfigReader;
|
||||
using pfaedle::eval::Collector;
|
||||
|
||||
std::string getMotStr(const MOTs& mots);
|
||||
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots);
|
||||
|
||||
// _____________________________________________________________________________
|
||||
int main(int argc, char** argv) {
|
||||
// disable output buffering for standard output
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
// initialize randomness
|
||||
srand(time(NULL) + rand()); // NOLINT
|
||||
|
||||
Config cfg;
|
||||
MotConfigReader motCfgReader;
|
||||
|
||||
ConfigReader cr;
|
||||
cr.read(&cfg, argc, argv);
|
||||
|
||||
ad::cppgtfs::gtfs::Feed gtfs;
|
||||
|
||||
motCfgReader.parse(cfg.configPaths);
|
||||
|
||||
if (cfg.feedPaths.size() == 1) {
|
||||
LOG(INFO) << "Reading " << cfg.feedPaths[0] << " ...";
|
||||
ad::cppgtfs::Parser p;
|
||||
p.parse(>fs, cfg.feedPaths[0]);
|
||||
LOG(INFO) << "Done.";
|
||||
} else if (cfg.feedPaths.size() > 1) {
|
||||
LOG(ERROR) << "Maximal one input feed allowed.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Read " << motCfgReader.getConfigs().size()
|
||||
<< " unique MOT configs.";
|
||||
MOTs cmdCfgMots = cfg.mots;
|
||||
ad::cppgtfs::gtfs::Trip* singleTrip = 0;
|
||||
|
||||
if (cfg.shapeTripId.size()) {
|
||||
singleTrip = gtfs.getTrips().get(cfg.shapeTripId);
|
||||
if (!singleTrip) {
|
||||
LOG(ERROR) << "Trip #" << cfg.shapeTripId << " not found.";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.writeOsm.size()) {
|
||||
LOG(INFO) << "Writing filtered XML to " << cfg.writeOsm << " ...";
|
||||
BBoxIdx box(2500);
|
||||
if (cfg.feedPaths.size()) {
|
||||
box = ShapeBuilder::getPaddedGtfsBox(>fs, 2500, cmdCfgMots,
|
||||
cfg.shapeTripId);
|
||||
}
|
||||
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.filterWrite(cfg.osmPath, cfg.writeOsm, opts, box);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
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()) {
|
||||
auto usedMots = getContMots(motCfg, cmdCfgMots);
|
||||
if (!usedMots.size()) continue;
|
||||
|
||||
std::string motStr = getMotStr(usedMots);
|
||||
LOG(INFO) << "Calculating shapes for mots " << motStr;
|
||||
|
||||
ShapeBuilder shapeBuilder(>fs, cmdCfgMots, motCfg, &ecoll, cfg);
|
||||
|
||||
if (cfg.writeGraph) {
|
||||
LOG(INFO) << "Outputting graph.json...";
|
||||
util::geo::output::GeoGraphJsonOutput out;
|
||||
std::ofstream fstr(cfg.dbgOutputPath + "/graph.json");
|
||||
out.print(*shapeBuilder.getGraph(), fstr);
|
||||
fstr.close();
|
||||
}
|
||||
|
||||
if (singleTrip) {
|
||||
LOG(INFO) << "Outputting path.json...";
|
||||
std::ofstream pstr(cfg.dbgOutputPath + "/path.json");
|
||||
util::geo::output::GeoJsonOutput o(pstr);
|
||||
|
||||
auto l = shapeBuilder.shapeL(singleTrip);
|
||||
|
||||
if (singleTrip->getShape()) {
|
||||
auto orig = Collector::getWebMercLine(singleTrip->getShape(), -1, -1);
|
||||
o.print(orig, {{"ver", "old"}});
|
||||
}
|
||||
|
||||
o.print(l, {{"ver", "new"}});
|
||||
o.flush();
|
||||
pstr.close();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
pfaedle::netgraph::Graph ng;
|
||||
shapeBuilder.shape(&ng);
|
||||
|
||||
if (cfg.buildTransitGraph) {
|
||||
LOG(INFO) << "Outputting trgraph.json...";
|
||||
util::geo::output::GeoGraphJsonOutput out;
|
||||
std::ofstream fstr(cfg.dbgOutputPath + "/trgraph.json");
|
||||
out.print(ng, fstr);
|
||||
fstr.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.evaluate) ecoll.printStats(&std::cout);
|
||||
|
||||
if (cfg.feedPaths.size()) {
|
||||
LOG(INFO) << "Writing output GTFS...";
|
||||
ad::cppgtfs::Writer w;
|
||||
w.write(>fs, cfg.outputPath);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string getMotStr(const MOTs& mots) {
|
||||
bool first = false;
|
||||
std::string motStr;
|
||||
for (const auto& mot : mots) {
|
||||
if (first) motStr += ", ";
|
||||
motStr += "<" + Route::getTypeString(mot) + ">";
|
||||
first = true;
|
||||
}
|
||||
|
||||
return motStr;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
MOTs getContMots(const MotConfig& motCfg, const MOTs& mots) {
|
||||
MOTs ret;
|
||||
for (const auto& mot : mots) {
|
||||
if (motCfg.mots.count(mot)) {
|
||||
ret.insert(mot);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
10
src/pfaedle/_config.h.in
Normal file
10
src/pfaedle/_config.h.in
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2017
|
||||
// Author: Patrick Brosi
|
||||
|
||||
#ifndef SRC_PFAEDLE_CONFIG_H_
|
||||
#define SRC_PFAEDLE_CONFIG_H_
|
||||
|
||||
// version number from cmake version module
|
||||
#define VERSION_FULL "@VERSION_GIT_FULL@"
|
||||
|
||||
#endif // SRC_PFAEDLE_CONFIG_H_N
|
201
src/pfaedle/config/ConfigReader.cpp
Normal file
201
src/pfaedle/config/ConfigReader.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <float.h>
|
||||
#include <getopt.h>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "pfaedle/_config.h"
|
||||
#include "pfaedle/config/ConfigReader.h"
|
||||
#include "util/String.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
using pfaedle::config::ConfigReader;
|
||||
|
||||
using std::string;
|
||||
using std::exception;
|
||||
using std::vector;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ConfigReader::help() {
|
||||
std::cout
|
||||
<< std::setfill(' ') << std::left
|
||||
<< "\033[1mpfaedle GTFS map matcher \033[22m\n"
|
||||
<< VERSION_FULL << " (built " << __DATE__ << " " << __TIME__ << ")\n\n"
|
||||
<< "(C) 2018 University of Freiburg\n"
|
||||
<< "Author: Patrick Brosi <brosi@informatik.uni-freiburg.de>\n\n"
|
||||
<< "Usage: "
|
||||
<< " -x <OSM FILE> -c <CFG FILE> <GTFS FEED>\n\n"
|
||||
<< "Allowed options:\n\n"
|
||||
<< "General:\n"
|
||||
<< std::setw(35) << " -v [ --version ]"
|
||||
<< "print version\n"
|
||||
<< std::setw(35) << " -h [ --help ]"
|
||||
<< "show this help message\n"
|
||||
<< "\nInput:\n"
|
||||
<< std::setw(35) << " -c [ --config ] arg"
|
||||
<< "pfaedle config file\n"
|
||||
<< std::setw(35) << " -i [ --input ] arg"
|
||||
<< "gtfs feed(s), may also be given as positional parameter (see usage)\n"
|
||||
<< std::setw(35) << " -x [ --osm-file ] arg"
|
||||
<< "OSM xml input file\n"
|
||||
<< std::setw(35) << " -m [ --mots ] arg (=all)"
|
||||
<< "MOTs to calculate shapes for, comma separated, either as string "
|
||||
"{all,\n"
|
||||
<< std::setw(35) << " "
|
||||
<< "tram | streetcar, subway | metro, rail | train, bus, ferry | boat | "
|
||||
"\n"
|
||||
<< std::setw(35) << " "
|
||||
<< "ship, cableclar, gondola, funicular} or as GTFS mot codes\n"
|
||||
<< "\nOutput:\n"
|
||||
<< std::setw(35) << " -o [ --output ] arg (=gtfs-out)"
|
||||
<< "GTFS output path\n"
|
||||
<< std::setw(35) << " -X [ --osm-out ] arg"
|
||||
<< "if specified, a filtered OSM file will be written to <arg>\n"
|
||||
<< "\nDebug Output:\n"
|
||||
<< std::setw(35) << " -d [ --dbg-path ] arg (=geo)"
|
||||
<< "output path for debug files\n"
|
||||
<< std::setw(35) << " --write-trgraph"
|
||||
<< "write transit graph as GeoJSON to <dbg-path>/trgraph.json\n"
|
||||
<< std::setw(35) << " --write-graph"
|
||||
<< "write routing graph as GeoJSON to <dbg-path>/graph.json\n"
|
||||
<< std::setw(35) << " --write-cgraph"
|
||||
<< "write combination graph as GeoJSON to <dbg-path>/combraph.json\n"
|
||||
<< std::setw(35) << " --method arg (=global)"
|
||||
<< "matching method to use, either 'global' (based on HMM), 'greedy' or "
|
||||
"'greedy2'\n"
|
||||
<< std::setw(35) << " --eval"
|
||||
<< "evaluate existing shapes against matched shapes and print results\n"
|
||||
<< std::setw(35) << " --eval-path arg (=.)"
|
||||
<< "path for eval file output\n"
|
||||
<< std::setw(35) << " --eval-df-bins arg (= )"
|
||||
<< "bins to use for d_f histogram, comma separated (e.g. 10,20,30,40)\n"
|
||||
<< "\nMisc:\n"
|
||||
<< std::setw(35) << " -T [ --trip-id ] arg"
|
||||
<< "Do routing only for trip <arg>, write result to\n"
|
||||
<< std::setw(35) << " "
|
||||
<< "<dbg-path>/path.json\n"
|
||||
<< std::setw(35) << " --grid-size arg (=2000)"
|
||||
<< "Grid cell size\n";
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ConfigReader::read(Config* cfg, int argc, char** argv) {
|
||||
std::string motStr = "all";
|
||||
bool printOpts = false;
|
||||
|
||||
struct option ops[] = {{"output", required_argument, 0, 'o'},
|
||||
{"input", required_argument, 0, 'i'},
|
||||
{"config", required_argument, 0, 'c'},
|
||||
{"osm-file", required_argument, 0, 'x'},
|
||||
{"drop-shapes", required_argument, 0, 'D'},
|
||||
{"mots", required_argument, NULL, 'm'},
|
||||
{"grid-size", required_argument, 0, 'g'},
|
||||
{"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'},
|
||||
{0, 0, 0, 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 5:
|
||||
cfg->solveMethod = optarg;
|
||||
break;
|
||||
case 6:
|
||||
cfg->evalPath = optarg;
|
||||
break;
|
||||
case 7:
|
||||
cfg->evalDfBins = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
cfg->outputPath = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
cfg->feedPaths.push_back(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
cfg->configPaths.push_back(optarg);
|
||||
break;
|
||||
case 'x':
|
||||
cfg->osmPath = optarg;
|
||||
break;
|
||||
case 'D':
|
||||
cfg->dropShapes = true;
|
||||
break;
|
||||
case 'm':
|
||||
motStr = optarg;
|
||||
break;
|
||||
case 'g':
|
||||
cfg->gridSize = atof(optarg);
|
||||
break;
|
||||
case 'X':
|
||||
cfg->writeOsm = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
cfg->shapeTripId = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
cfg->dbgOutputPath = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << VERSION_FULL << " (built " << __DATE__ << " " << __TIME__
|
||||
<< ")\n\n";
|
||||
exit(0);
|
||||
case 'p':
|
||||
printOpts = true;
|
||||
break;
|
||||
case 'h':
|
||||
help();
|
||||
exit(0);
|
||||
case ':':
|
||||
std::cerr << argv[optind - 1];
|
||||
std::cerr << " requires an argument" << std::endl;
|
||||
exit(1);
|
||||
case '?':
|
||||
std::cerr << argv[optind - 1];
|
||||
std::cerr << " option unknown" << std::endl;
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Error while parsing arguments" << std::endl;
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = optind; i < argc; i++) cfg->feedPaths.push_back(argv[i]);
|
||||
|
||||
auto v = util::split(motStr, ',');
|
||||
for (const auto& motStr : v) {
|
||||
const auto& mots = Route::getTypesFromString(util::trim(motStr));
|
||||
cfg->mots.insert(mots.begin(), mots.end());
|
||||
}
|
||||
|
||||
if (printOpts)
|
||||
std::cout << "\nConfigured options:\n\n" << cfg->toString() << std::endl;
|
||||
}
|
21
src/pfaedle/config/ConfigReader.h
Normal file
21
src/pfaedle/config/ConfigReader.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_CONFIG_CONFIGREADER_H_
|
||||
#define PFAEDLE_CONFIG_CONFIGREADER_H_
|
||||
|
||||
#include <vector>
|
||||
#include "pfaedle/config/PfaedleConfig.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace config {
|
||||
|
||||
class ConfigReader {
|
||||
public:
|
||||
static void read(Config* targetConfig, int argc, char** argv);
|
||||
static void help();
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // PFAEDLE_CONFIG_CONFIGREADER_H_
|
28
src/pfaedle/config/MotConfig.h
Normal file
28
src/pfaedle/config/MotConfig.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_CONFIG_MOTCONFIG_H_
|
||||
#define PFAEDLE_CONFIG_MOTCONFIG_H_
|
||||
|
||||
#include "pfaedle/osm/OsmBuilder.h"
|
||||
#include "pfaedle/router/Router.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace config {
|
||||
|
||||
|
||||
struct MotConfig {
|
||||
router::MOTs mots;
|
||||
osm::OsmReadOpts osmBuildOpts;
|
||||
router::RoutingOpts routingOpts;
|
||||
};
|
||||
|
||||
inline bool operator==(const MotConfig& a, const MotConfig& b) {
|
||||
return a.osmBuildOpts == b.osmBuildOpts && a.routingOpts == b.routingOpts;
|
||||
}
|
||||
|
||||
} // namespace config
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_CONFIG_MOTCONFIG_H_
|
463
src/pfaedle/config/MotConfigReader.cpp
Normal file
463
src/pfaedle/config/MotConfigReader.cpp
Normal file
|
@ -0,0 +1,463 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "pfaedle/config/MotConfigReader.h"
|
||||
#include "util/Misc.h"
|
||||
#include "util/String.h"
|
||||
|
||||
using pfaedle::config::MotConfigReader;
|
||||
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;
|
||||
using ad::cppgtfs::gtfs::Route;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
MotConfigReader::MotConfigReader() {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void MotConfigReader::parse(const std::vector<std::string>& paths) {
|
||||
for (const auto& s : paths) {
|
||||
ConfigFileParser p;
|
||||
p.parse(s);
|
||||
|
||||
for (const auto& sec : p.getSecs()) {
|
||||
MotConfig curCfg;
|
||||
std::string secStr = sec.first;
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_keep")) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_keep", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.keepFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
std::string name =
|
||||
std::string("osm_filter_lvl") + std::to_string(i + 1);
|
||||
if (p.hasKey(secStr, name)) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, name, ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.levelFilters[i][fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_drop")) {
|
||||
for (const auto& kvs : p.getStrArr(sec.first, "osm_filter_drop", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.dropFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_snap_level")) {
|
||||
curCfg.osmBuildOpts.maxSnapLevel =
|
||||
p.getInt(sec.first, "osm_max_snap_level");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxSnapLevel = 7;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_nohup")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_nohup", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.noHupFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_oneway")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_oneway", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.oneWayFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_oneway_reverse")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_oneway_reverse", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.oneWayFilterRev[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_undirected")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_undirected", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.twoWayFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_station")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_station", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.stationFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_station_blocker")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_station_blocker", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.stationBlockerFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_node_positive_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_node_positive_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.restrPosRestr[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_node_negative_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_node_negative_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.restrNegRestr[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_filter_no_restriction")) {
|
||||
for (const auto& kvs :
|
||||
p.getStrArr(sec.first, "osm_filter_no_restriction", ' ')) {
|
||||
auto fRule = getFRule(kvs);
|
||||
curCfg.osmBuildOpts.noRestrFilter[fRule.kv.first].insert(
|
||||
osm::AttrFlagPair(fRule.kv.second, getFlags(fRule.flags)));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_station_name_attrs")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_station_name_attrs", ' ')) {
|
||||
curCfg.osmBuildOpts.statAttrRules.nameRule.push_back(
|
||||
getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_track_number_tags")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_track_number_tags", ' ')) {
|
||||
curCfg.osmBuildOpts.statAttrRules.platformRule.push_back(
|
||||
getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_edge_track_number_tags")) {
|
||||
for (const std::string& r :
|
||||
p.getStrArr(sec.first, "osm_edge_track_number_tags", ' ')) {
|
||||
curCfg.osmBuildOpts.edgePlatformRules.push_back(getDeepAttrRule(r));
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_station_group_attrs")) {
|
||||
auto arr = p.getStrArr(secStr, "osm_station_group_attrs", ' ');
|
||||
|
||||
for (const auto& ruleStr : arr) {
|
||||
auto deep = getDeepAttrRule(ruleStr);
|
||||
// TODO(patrick): getKv is misused here as a a=b parser
|
||||
auto attrD = getKv(deep.attr);
|
||||
deep.attr = attrD.first;
|
||||
double dist = atof(attrD.second.c_str());
|
||||
curCfg.osmBuildOpts.statGroupNAttrRules.push_back({deep, dist});
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_line_relation_tags")) {
|
||||
auto arr = p.getStrArr(secStr, "osm_line_relation_tags", ' ');
|
||||
|
||||
for (const auto& ruleStr : arr) {
|
||||
auto rule = getKv(ruleStr);
|
||||
auto tags = util::split(rule.second, ',');
|
||||
if (rule.first == "from_name")
|
||||
curCfg.osmBuildOpts.relLinerules.fromNameRule = tags;
|
||||
else if (rule.first == "to_name")
|
||||
curCfg.osmBuildOpts.relLinerules.toNameRule = tags;
|
||||
else if (rule.first == "line_name")
|
||||
curCfg.osmBuildOpts.relLinerules.sNameRule = tags;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_snap_distance")) {
|
||||
curCfg.osmBuildOpts.maxSnapDistances =
|
||||
p.getDoubleArr(secStr, "osm_max_snap_distance", ',');
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxSnapDistances.push_back(50);
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_snap_fallback_distance")) {
|
||||
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
|
||||
p.getDouble(secStr, "osm_max_snap_fallback_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxSnapFallbackHeurDistance =
|
||||
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
|
||||
curCfg.osmBuildOpts.maxSnapDistances.end()) *
|
||||
2;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_group_search_distance")) {
|
||||
curCfg.osmBuildOpts.maxGroupSearchDistance =
|
||||
p.getDouble(secStr, "osm_max_group_search_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxGroupSearchDistance =
|
||||
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
|
||||
curCfg.osmBuildOpts.maxSnapDistances.end()) *
|
||||
4;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_osm_station_distance")) {
|
||||
curCfg.osmBuildOpts.maxOsmStationDistance =
|
||||
p.getDouble(secStr, "osm_max_osm_station_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxOsmStationDistance = 5;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "osm_max_node_block_distance")) {
|
||||
curCfg.osmBuildOpts.maxBlockDistance =
|
||||
p.getDouble(secStr, "osm_max_node_block_distance");
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxBlockDistance =
|
||||
*std::max_element(curCfg.osmBuildOpts.maxSnapDistances.begin(),
|
||||
curCfg.osmBuildOpts.maxSnapDistances.end()) /
|
||||
8;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
std::string name =
|
||||
std::string("routing_lvl") + std::to_string(i) + "_fac";
|
||||
if (p.hasKey(secStr, name)) {
|
||||
double v = p.getDouble(sec.first, name);
|
||||
curCfg.routingOpts.levelPunish[i] = v;
|
||||
} else {
|
||||
curCfg.routingOpts.levelPunish[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "routing_full_turn_punish")) {
|
||||
curCfg.routingOpts.fullTurnPunishFac =
|
||||
p.getDouble(secStr, "routing_full_turn_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_full_turn_angle")) {
|
||||
double ang = p.getDouble(secStr, "routing_full_turn_angle");
|
||||
curCfg.routingOpts.fullTurnAngle = ang;
|
||||
} else {
|
||||
curCfg.routingOpts.fullTurnAngle = 5;
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_snap_full_turn_angle")) {
|
||||
double ang = p.getDouble(secStr, "routing_snap_full_turn_angle");
|
||||
curCfg.osmBuildOpts.maxAngleSnapReach = ang;
|
||||
} else {
|
||||
curCfg.osmBuildOpts.maxAngleSnapReach =
|
||||
curCfg.routingOpts.fullTurnAngle;
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_pass_thru_station_punish")) {
|
||||
curCfg.routingOpts.passThruStationsPunish =
|
||||
p.getDouble(secStr, "routing_pass_thru_station_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_one_way_meter_punish_fac")) {
|
||||
curCfg.routingOpts.oneWayPunishFac =
|
||||
p.getDouble(secStr, "routing_one_way_meter_punish_fac");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_one_way_edge_punish")) {
|
||||
curCfg.routingOpts.oneWayEdgePunish =
|
||||
p.getDouble(secStr, "routing_one_way_edge_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_line_unmatched_punish_fac")) {
|
||||
curCfg.routingOpts.lineUnmatchedPunishFact =
|
||||
p.getDouble(secStr, "routing_line_unmatched_punish_fac");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_platform_unmatched_punish")) {
|
||||
curCfg.routingOpts.platformUnmatchedPen =
|
||||
p.getDouble(secStr, "routing_platform_unmatched_punish");
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_non_osm_station_punish")) {
|
||||
curCfg.routingOpts.nonOsmPen =
|
||||
p.getDouble(secStr, "routing_non_osm_station_punish");
|
||||
} else {
|
||||
curCfg.routingOpts.nonOsmPen = 0;
|
||||
}
|
||||
if (p.hasKey(secStr, "routing_station_distance_punish_fac")) {
|
||||
curCfg.routingOpts.stationDistPenFactor =
|
||||
p.getDouble(secStr, "routing_station_distance_punish_fac");
|
||||
} else {
|
||||
curCfg.routingOpts.stationDistPenFactor = 1;
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "station_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "station_normalize_chain", ';');
|
||||
curCfg.osmBuildOpts.statNormzer =
|
||||
trgraph::Normalizer(getNormRules(arr));
|
||||
} catch (const std::exception& e) {
|
||||
throw ParseExc(p.getVal(secStr, "station_normalize_chain").line,
|
||||
p.getVal(secStr, "station_normalize_chain").pos,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">", s);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "track_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "track_normalize_chain", ';');
|
||||
curCfg.osmBuildOpts.trackNormzer =
|
||||
trgraph::Normalizer(getNormRules(arr));
|
||||
} catch (const std::exception& e) {
|
||||
throw ParseExc(p.getVal(secStr, "track_normalize_chain").line,
|
||||
p.getVal(secStr, "station_normalize_chain").pos,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">", s);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.hasKey(secStr, "line_normalize_chain")) {
|
||||
try {
|
||||
auto arr = p.getStrArr(secStr, "line_normalize_chain", ';');
|
||||
curCfg.osmBuildOpts.lineNormzer =
|
||||
trgraph::Normalizer(getNormRules(arr));
|
||||
} catch (const std::exception& e) {
|
||||
throw ParseExc(p.getVal(secStr, "station_normalize_chain").line,
|
||||
p.getVal(secStr, "station_normalize_chain").pos,
|
||||
"<valid regular expression>",
|
||||
std::string("<regex error: ") + e.what() + ">", s);
|
||||
}
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (auto& cfg : _cfgs) {
|
||||
if (cfg == curCfg) {
|
||||
for (auto mot : Route::getTypesFromString(secStr)) {
|
||||
cfg.mots.insert(mot);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
curCfg.mots = Route::getTypesFromString(secStr);
|
||||
_cfgs.push_back(curCfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
ReplRules MotConfigReader::getNormRules(
|
||||
const std::vector<std::string>& arr) const {
|
||||
trgraph::ReplRules ret;
|
||||
|
||||
for (auto a : arr) {
|
||||
size_t p = a.find(" -> ");
|
||||
if (p == std::string::npos) continue;
|
||||
|
||||
trgraph::ReplRule r;
|
||||
r.first = a.substr(0, p);
|
||||
r.second = a.substr(p + 4, std::string::npos);
|
||||
|
||||
if (r.first.size() > 1 && r.first.front() == '\'' &&
|
||||
r.first.back() == '\'') {
|
||||
r.first = r.first.substr(1, r.first.size() - 2);
|
||||
}
|
||||
|
||||
if (r.second.size() > 1 && r.second.front() == '\'' &&
|
||||
r.second.back() == '\'') {
|
||||
r.second = r.second.substr(1, r.second.size() - 2);
|
||||
}
|
||||
|
||||
ret.push_back(r);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t MotConfigReader::getFlags(const std::set<string>& flags) const {
|
||||
uint64_t ret = osm::USE;
|
||||
|
||||
for (const auto& flag : flags) {
|
||||
if (flag == "rel_flat") {
|
||||
ret |= osm::REL_NO_DOWN;
|
||||
continue;
|
||||
}
|
||||
if (flag == "no_match_nds") {
|
||||
ret |= osm::NO_NODES;
|
||||
continue;
|
||||
}
|
||||
if (flag == "no_match_rels") {
|
||||
ret |= osm::NO_RELATIONS;
|
||||
continue;
|
||||
}
|
||||
if (flag == "no_match_ways") {
|
||||
ret |= osm::NO_WAYS;
|
||||
continue;
|
||||
}
|
||||
if (flag == "mult_val_match") {
|
||||
ret |= osm::MULT_VAL_MATCH;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FilterRule MotConfigReader::getFRule(const std::string& r) const {
|
||||
osm::FilterRule ret;
|
||||
|
||||
auto parts = util::split(util::trim(r), '|');
|
||||
|
||||
ret.kv = getKv(parts[0]);
|
||||
ret.flags = std::set<std::string>(parts.begin() + 1, parts.end());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
KeyVal MotConfigReader::getKv(const std::string& kv) const {
|
||||
osm::KeyVal ret;
|
||||
size_t p = kv.find('=', 0);
|
||||
ret.first = kv.substr(0, p);
|
||||
|
||||
if (p != std::string::npos) {
|
||||
ret.second = kv.substr(p + 1, std::string::npos);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::vector<MotConfig>& MotConfigReader::getConfigs() const {
|
||||
return _cfgs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
DeepAttrRule MotConfigReader::getDeepAttrRule(const std::string& rule) const {
|
||||
if (rule[0] == '[' && rule.find(']') != std::string::npos) {
|
||||
auto kv = getFRule(rule.substr(1, rule.find(']') - 1));
|
||||
std::string attr = rule.substr(rule.find(']') + 1);
|
||||
return osm::DeepAttrRule{attr, kv};
|
||||
} else {
|
||||
return osm::DeepAttrRule{rule, osm::FilterRule()};
|
||||
}
|
||||
}
|
42
src/pfaedle/config/MotConfigReader.h
Normal file
42
src/pfaedle/config/MotConfigReader.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_CONFIG_MOTCONFIGREADER_H_
|
||||
#define PFAEDLE_CONFIG_MOTCONFIGREADER_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "ad/cppgtfs/gtfs/Route.h"
|
||||
#include "configparser/ConfigFileParser.h"
|
||||
#include "pfaedle/config/MotConfig.h"
|
||||
#include "pfaedle/osm/OsmBuilder.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace config {
|
||||
|
||||
using ad::cppgtfs::gtfs::Route;
|
||||
|
||||
class MotConfigReader {
|
||||
public:
|
||||
MotConfigReader();
|
||||
void parse(const std::vector<std::string>& paths);
|
||||
|
||||
const std::vector<MotConfig>& getConfigs() const;
|
||||
|
||||
private:
|
||||
std::vector<MotConfig> _cfgs;
|
||||
|
||||
osm::KeyVal getKv(const std::string& kv) const;
|
||||
osm::FilterRule getFRule(const std::string& kv) const;
|
||||
|
||||
trgraph::ReplRules getNormRules(const std::vector<std::string>& arr) const;
|
||||
osm::DeepAttrRule getDeepAttrRule(const std::string& rule) const;
|
||||
uint64_t getFlags(const std::set<string>& flags) const;
|
||||
};
|
||||
} // namespace config
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_CONFIG_MOTCONFIGREADER_H_
|
89
src/pfaedle/config/PfaedleConfig.h
Normal file
89
src/pfaedle/config/PfaedleConfig.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_CONFIG_PFAEDLECONFIG_H_
|
||||
#define PFAEDLE_CONFIG_PFAEDLECONFIG_H_
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include "ad/cppgtfs/gtfs/Route.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace config {
|
||||
|
||||
using ad::cppgtfs::gtfs::Route;
|
||||
|
||||
struct Config {
|
||||
Config()
|
||||
: dbgOutputPath("geo"),
|
||||
solveMethod("global"),
|
||||
evalPath("."),
|
||||
dropShapes(false),
|
||||
useHMM(false),
|
||||
writeGraph(false),
|
||||
writeCombGraph(false),
|
||||
evaluate(false),
|
||||
buildTransitGraph(false),
|
||||
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 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;
|
||||
double gridSize;
|
||||
|
||||
std::string toString() {
|
||||
std::stringstream ss;
|
||||
ss << "trip-id: " << shapeTripId << "\n"
|
||||
<< "output-path: " << outputPath << "\n"
|
||||
<< "write-osm-path: " << writeOsm << "\n"
|
||||
<< "read-osm-path: " << osmPath << "\n"
|
||||
<< "debug-output-path: " << dbgOutputPath << "\n"
|
||||
<< "drop-shapes: " << dropShapes << "\n"
|
||||
<< "use-hmm: " << useHMM << "\n"
|
||||
<< "write-graph: " << writeGraph << "\n"
|
||||
<< "write-cgraph: " << writeCombGraph << "\n"
|
||||
<< "grid-size: " << gridSize << "\n"
|
||||
<< "feed-paths: ";
|
||||
|
||||
for (const auto& p : feedPaths) {
|
||||
ss << p << " ";
|
||||
}
|
||||
|
||||
ss << "\nconfig-paths: ";
|
||||
|
||||
for (const auto& p : configPaths) {
|
||||
ss << p << " ";
|
||||
}
|
||||
|
||||
ss << "\nmots: ";
|
||||
|
||||
for (const auto& mot : mots) {
|
||||
ss << mot << " ";
|
||||
}
|
||||
|
||||
ss << "\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace config
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_CONFIG_PFAEDLECONFIG_H_
|
417
src/pfaedle/eval/Collector.cpp
Normal file
417
src/pfaedle/eval/Collector.cpp
Normal 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/eval/Collector.h"
|
||||
#include "pfaedle/eval/Result.h"
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/geo/PolyLine.h"
|
||||
#include "util/geo/output/GeoJsonOutput.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
using util::geo::FLine;
|
||||
using util::geo::PolyLine;
|
||||
using util::geo::FPoint;
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
using ad::cppgtfs::gtfs::Shape;
|
||||
using pfaedle::eval::Collector;
|
||||
using pfaedle::eval::Result;
|
||||
using util::geo::output::GeoJsonOutput;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double Collector::add(const Trip* t, const Shape* oldS, const Shape* newS,
|
||||
const std::vector<double>& newTripDists) {
|
||||
if (!oldS) {
|
||||
_noOrigShp++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto st : t->getStopTimes()) {
|
||||
if (st.getShapeDistanceTravelled() < 0) {
|
||||
// we cannot safely compare trips without shape dist travelled
|
||||
// info
|
||||
_noOrigShp++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double fd = 0;
|
||||
size_t unmatchedSegments;
|
||||
double unmatchedSegmentsLength;
|
||||
|
||||
std::vector<double> oldDists;
|
||||
FLine oldL = getWebMercLine(
|
||||
oldS, t->getStopTimes().begin()->getShapeDistanceTravelled(),
|
||||
(--t->getStopTimes().end())->getShapeDistanceTravelled(), &oldDists);
|
||||
|
||||
std::vector<double> newDists;
|
||||
FLine newL = getWebMercLine(newS, -1, -1, &newDists);
|
||||
|
||||
std::ofstream fstr(_evalOutPath + "/trip-" + t->getId() + ".json");
|
||||
GeoJsonOutput gjout(fstr);
|
||||
|
||||
auto oldSegs = segmentize(t, oldL, oldDists, 0);
|
||||
auto newSegs = segmentize(t, newL, newDists, &newTripDists);
|
||||
|
||||
// cut both result at the beginning and end to clear evaluation from
|
||||
// loops at the end
|
||||
PolyLine<float> oldStart = oldSegs[0];
|
||||
PolyLine<float> newStart = newSegs[0];
|
||||
auto oldStartNew =
|
||||
oldStart.getSegment(oldStart.projectOn(newSegs[0][0]).totalPos, 1);
|
||||
auto newStartNew =
|
||||
newStart.getSegment(newStart.projectOn(oldSegs[0][0]).totalPos, 1);
|
||||
if (fabs(oldStartNew.getLength() - oldStart.getLength()) /
|
||||
oldStart.getLength() <
|
||||
0.5 &&
|
||||
fabs(newStartNew.getLength() - newStart.getLength()) /
|
||||
newStart.getLength() <
|
||||
0.5) {
|
||||
oldSegs[0] = oldStartNew.getLine();
|
||||
newSegs[0] = newStartNew.getLine();
|
||||
}
|
||||
|
||||
PolyLine<float> oldEnd = oldSegs[oldSegs.size() - 1];
|
||||
PolyLine<float> newEnd = newSegs[oldSegs.size() - 1];
|
||||
auto oldEndNew =
|
||||
oldEnd.getSegment(0, oldEnd.projectOn(newSegs.back().back()).totalPos);
|
||||
auto newEndNew =
|
||||
newEnd.getSegment(0, newEnd.projectOn(oldSegs.back().back()).totalPos);
|
||||
if (fabs(oldEndNew.getLength() - oldEnd.getLength()) / oldEnd.getLength() <
|
||||
0.5 &&
|
||||
fabs(newEndNew.getLength() - newEnd.getLength()) / newEnd.getLength() <
|
||||
0.5) {
|
||||
oldSegs[oldSegs.size() - 1] = oldEndNew.getLine();
|
||||
newSegs[newSegs.size() - 1] = newEndNew.getLine();
|
||||
}
|
||||
|
||||
// check for suspicious (most likely erroneous) lines in the
|
||||
// ground truth data which have a long straight-line segment
|
||||
|
||||
for (auto oldL : oldSegs) {
|
||||
for (size_t i = 1; i < oldL.size(); i++) {
|
||||
if (util::geo::webMercMeterDist(oldL[i - 1], oldL[i]) > 500) {
|
||||
// return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// new lines build from cleaned-up shapes
|
||||
FLine oldLCut;
|
||||
FLine newLCut;
|
||||
|
||||
for (auto oldL : oldSegs) {
|
||||
gjout.print(oldL, {{"ver", "old"}});
|
||||
oldLCut.insert(oldLCut.end(), oldL.begin(), oldL.end());
|
||||
}
|
||||
for (auto newL : newSegs) {
|
||||
gjout.print(newL, {{"ver", "new"}});
|
||||
newLCut.insert(newLCut.end(), newL.begin(), newL.end());
|
||||
}
|
||||
|
||||
gjout.flush();
|
||||
fstr.close();
|
||||
|
||||
double fac = cos(2 * atan(exp((oldSegs.front().front().get<1>() +
|
||||
oldSegs.back().back().get<1>()) /
|
||||
6378137.0)) -
|
||||
1.5707965);
|
||||
|
||||
if (_dCache.count(oldS) && _dCache.find(oldS)->second.count(newS)) {
|
||||
fd = _dCache[oldS][newS];
|
||||
} else {
|
||||
fd = util::geo::accFrechetDistC(oldLCut, newLCut, 5 / fac) * fac;
|
||||
_dCache[oldS][newS] = fd;
|
||||
}
|
||||
|
||||
if (_dACache.count(oldS) && _dACache.find(oldS)->second.count(newS)) {
|
||||
unmatchedSegments = _dACache[oldS][newS].first;
|
||||
unmatchedSegmentsLength = _dACache[oldS][newS].second;
|
||||
} else {
|
||||
auto dA = getDa(oldSegs, newSegs);
|
||||
_dACache[oldS][newS] = dA;
|
||||
unmatchedSegments = dA.first;
|
||||
unmatchedSegmentsLength = dA.second;
|
||||
}
|
||||
|
||||
double totL = 0;
|
||||
for (auto l : oldSegs) totL += util::geo::len(l) * fac;
|
||||
|
||||
// filter out shapes with a lenght of under 5 meters - they are most likely
|
||||
// artifacts
|
||||
if (totL < 5) {
|
||||
_noOrigShp++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_fdSum += fd / totL;
|
||||
_unmatchedSegSum += unmatchedSegments;
|
||||
_unmatchedSegLengthSum += unmatchedSegmentsLength;
|
||||
_results.insert(Result(t, fd / totL));
|
||||
_resultsAN.insert(Result(t, static_cast<double>(unmatchedSegments) /
|
||||
static_cast<double>(oldSegs.size())));
|
||||
_resultsAL.insert(Result(t, unmatchedSegmentsLength / totL));
|
||||
|
||||
LOG(DEBUG) << "This result (" << t->getId()
|
||||
<< "): A_N/N = " << unmatchedSegments << "/" << oldSegs.size()
|
||||
<< " = "
|
||||
<< static_cast<double>(unmatchedSegments) /
|
||||
static_cast<double>(oldSegs.size())
|
||||
<< " A_L/L = " << unmatchedSegmentsLength << "/" << totL << " = "
|
||||
<< unmatchedSegmentsLength / totL << " d_f = " << fd;
|
||||
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::vector<FLine> Collector::segmentize(
|
||||
const Trip* t, const FLine& shape, const std::vector<double>& dists,
|
||||
const std::vector<double>* newTripDists) {
|
||||
std::vector<FLine> ret;
|
||||
|
||||
if (t->getStopTimes().size() < 2) return ret;
|
||||
|
||||
util::geo::PolyLine<float> pl(shape);
|
||||
std::vector<std::pair<FPoint, double> > cuts;
|
||||
|
||||
size_t i = 0;
|
||||
for (auto st : t->getStopTimes()) {
|
||||
if (newTripDists) {
|
||||
cuts.push_back(std::pair<FPoint, double>(
|
||||
util::geo::latLngToWebMerc<float>(st.getStop()->getLat(),
|
||||
st.getStop()->getLng()),
|
||||
(*newTripDists)[i]));
|
||||
} else {
|
||||
cuts.push_back(std::pair<FPoint, double>(
|
||||
util::geo::latLngToWebMerc<float>(st.getStop()->getLat(),
|
||||
st.getStop()->getLng()),
|
||||
st.getShapeDistanceTravelled()));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// get first half of geometry, and search for start point there!
|
||||
size_t before = std::upper_bound(dists.begin(), dists.end(), cuts[1].second) -
|
||||
dists.begin();
|
||||
util::geo::PolyLine<float> l(
|
||||
FLine(shape.begin(), shape.begin() + before + 1));
|
||||
auto lastLp = l.projectOn(cuts.front().first);
|
||||
|
||||
for (size_t i = 1; i < cuts.size(); i++) {
|
||||
size_t before = shape.size();
|
||||
if (i < cuts.size() - 1 && cuts[i + 1].second > -0.5) {
|
||||
before =
|
||||
std::upper_bound(dists.begin(), dists.end(), cuts[i + 1].second) -
|
||||
dists.begin();
|
||||
}
|
||||
|
||||
util::geo::PolyLine<float> beforePl(
|
||||
FLine(shape.begin(), shape.begin() + before));
|
||||
|
||||
auto curLp = beforePl.projectOnAfter(cuts[i].first, lastLp.lastIndex);
|
||||
|
||||
ret.push_back(pl.getSegment(lastLp, curLp).getLine());
|
||||
lastLp = curLp;
|
||||
}
|
||||
|
||||
// std::raise(SIGABRT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FLine Collector::getWebMercLine(const Shape* s, double from, double t) {
|
||||
return getWebMercLine(s, from, t, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FLine Collector::getWebMercLine(const Shape* s, double from, double to,
|
||||
std::vector<double>* dists) {
|
||||
FLine ret;
|
||||
|
||||
auto i = s->getPoints().begin();
|
||||
|
||||
for (; i != s->getPoints().end(); i++) {
|
||||
auto p = *i;
|
||||
|
||||
if ((from < 0 || (p.travelDist - from) > -0.01)) {
|
||||
if (to >= 0 && (p.travelDist - to) > 0.01) break;
|
||||
|
||||
FPoint mercP = util::geo::latLngToWebMerc<float>(p.lat, p.lng);
|
||||
|
||||
ret.push_back(mercP);
|
||||
if (dists) dists->push_back(p.travelDist);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<Result>& Collector::getResults() const { return _results; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double Collector::getAvgDist() const { return _fdSum / _results.size(); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Collector::printHisto(std::ostream* os, const std::set<Result>& result,
|
||||
const std::vector<double>& bins) const {
|
||||
size_t W = 60;
|
||||
|
||||
auto it = result.begin();
|
||||
std::vector<std::pair<double, size_t> > res;
|
||||
std::vector<const Trip*> examples;
|
||||
size_t maxC = 0;
|
||||
|
||||
for (size_t i = 0; i < bins.size(); i++) {
|
||||
size_t c = 0;
|
||||
const Trip* trip = 0;
|
||||
|
||||
while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) {
|
||||
if (!trip) trip = it->getTrip();
|
||||
c++;
|
||||
it++;
|
||||
}
|
||||
|
||||
if (c > maxC) maxC = c;
|
||||
|
||||
examples.push_back(trip);
|
||||
res.push_back(std::pair<double, size_t>(bins[i], c));
|
||||
}
|
||||
|
||||
size_t j = 0;
|
||||
for (auto r : res) {
|
||||
std::string range = util::toString(r.first);
|
||||
(*os) << " < " << std::setfill(' ') << std::setw(10) << range << ": ";
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < W * (static_cast<double>(r.second) / static_cast<double>(maxC));
|
||||
i++) {
|
||||
(*os) << "|";
|
||||
}
|
||||
|
||||
if (r.second)
|
||||
(*os) << " (" << r.second << ", e.g. #" << examples[j]->getId() << ")";
|
||||
(*os) << std::endl;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::vector<double> Collector::getBins(double mind, double maxd, size_t steps) {
|
||||
double bin = (maxd - mind) / steps;
|
||||
double curE = mind + bin;
|
||||
|
||||
std::vector<double> ret;
|
||||
while (curE <= maxd) {
|
||||
ret.push_back(curE);
|
||||
curE += bin;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Collector::printCsv(std::ostream* os, const std::set<Result>& result,
|
||||
const std::vector<double>& bins) const {
|
||||
auto it = result.begin();
|
||||
std::vector<std::pair<double, size_t> > res;
|
||||
|
||||
for (size_t i = 0; i < bins.size(); i++) {
|
||||
size_t c = 0;
|
||||
const Trip* trip = 0;
|
||||
|
||||
while (it != result.end() && it->getDist() <= (bins[i] + 0.001)) {
|
||||
if (!trip) trip = it->getTrip();
|
||||
c++;
|
||||
it++;
|
||||
}
|
||||
|
||||
res.push_back(std::pair<double, size_t>(bins[i], c));
|
||||
}
|
||||
|
||||
(*os) << "range, count\n";
|
||||
for (auto r : res) {
|
||||
(*os) << r.first << "," << r.second << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Collector::printStats(std::ostream* os) const {
|
||||
size_t buckets = 10;
|
||||
(*os) << "\n ===== Evalution results =====\n\n";
|
||||
|
||||
(*os) << std::setfill(' ') << std::setw(30)
|
||||
<< " # of trips new shapes were matched for: " << _results.size()
|
||||
<< "\n";
|
||||
(*os) << std::setw(30) << " # of trips without input shapes: " << _noOrigShp
|
||||
<< "\n";
|
||||
|
||||
if (_results.size()) {
|
||||
(*os) << std::setw(30) << " highest distance to input shapes: "
|
||||
<< (--_results.end())->getDist() << " (on trip #"
|
||||
<< (--_results.end())->getTrip()->getId() << ")\n";
|
||||
(*os) << std::setw(30) << " lowest distance to input shapes: "
|
||||
<< (_results.begin())->getDist() << " (on trip #"
|
||||
<< (_results.begin())->getTrip()->getId() << ")\n";
|
||||
(*os) << std::setw(30) << " avg total frechet distance: " << getAvgDist()
|
||||
<< "\n";
|
||||
|
||||
std::vector<double> dfBins = getBins(
|
||||
(_results.begin())->getDist(), (--_results.end())->getDist(), buckets);
|
||||
|
||||
if (_dfBins.size()) dfBins = _dfBins;
|
||||
|
||||
(*os) << "\n -- Histogram of d_f for this run -- " << std::endl;
|
||||
printHisto(os, _results, dfBins);
|
||||
|
||||
std::ofstream fstr1(_evalOutPath + "/eval-frechet.csv");
|
||||
printCsv(&fstr1, _results, dfBins);
|
||||
|
||||
(*os) << "\n\n\n -- Histogram of A_N/N for this run -- " << std::endl;
|
||||
printHisto(os, _resultsAN,
|
||||
getBins((_resultsAN.begin())->getDist(),
|
||||
(--_resultsAN.end())->getDist(), buckets));
|
||||
std::ofstream fstr2(_evalOutPath + "/eval-AN.csv");
|
||||
printCsv(&fstr2, _resultsAN, getBins(0, 1, 20));
|
||||
|
||||
(*os) << "\n\n\n -- Histogram of A_L/L for this run -- " << std::endl;
|
||||
printHisto(os, _resultsAL,
|
||||
getBins((_resultsAL.begin())->getDist(),
|
||||
(--_resultsAL.end())->getDist(), buckets));
|
||||
std::ofstream fstr3(_evalOutPath + "/eval-AL.csv");
|
||||
printCsv(&fstr3, _resultsAL, getBins(0, 1, 20));
|
||||
}
|
||||
|
||||
(*os) << "\n ===== End of evaluation results =====\n";
|
||||
(*os) << std::endl;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::pair<size_t, double> Collector::getDa(const std::vector<FLine>& a,
|
||||
const std::vector<FLine>& b) {
|
||||
assert(a.size() == b.size());
|
||||
std::pair<size_t, double> ret{0, 0};
|
||||
|
||||
// euclidean distance on web mercator is in meters on equator,
|
||||
// and proportional to cos(lat) in both y directions
|
||||
double fac =
|
||||
cos(2 * atan(exp((a.front().front().get<1>() + a.back().back().get<1>()) /
|
||||
6378137.0)) -
|
||||
1.5707965);
|
||||
|
||||
for (size_t i = 0; i < a.size(); i++) {
|
||||
double fd = util::geo::frechetDist(a[i], b[i], 3 / fac) * fac;
|
||||
if (fd >= 20) {
|
||||
ret.first++;
|
||||
ret.second += util::geo::len(a[i]) * fac;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
96
src/pfaedle/eval/Collector.h
Normal file
96
src/pfaedle/eval/Collector.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_EVAL_COLLECTOR_H_
|
||||
#define PFAEDLE_EVAL_COLLECTOR_H_
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/eval/Result.h"
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
using ad::cppgtfs::gtfs::Shape;
|
||||
using util::geo::FLine;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace eval {
|
||||
|
||||
/*
|
||||
* Collects routing results for evaluation
|
||||
*/
|
||||
class Collector {
|
||||
public:
|
||||
Collector(const std::string& evalOutPath, const std::vector<double>& dfBins)
|
||||
: _noOrigShp(0),
|
||||
_noMatchShp(0),
|
||||
_fdSum(0),
|
||||
_unmatchedSegSum(0),
|
||||
_unmatchedSegLengthSum(0),
|
||||
_evalOutPath(evalOutPath),
|
||||
_dfBins(dfBins) {}
|
||||
|
||||
// Add a shape found by our tool newS for a trip t with newly calculated
|
||||
// station dist values with the old shape oldS
|
||||
double add(const Trip* t, const Shape* oldS, const Shape* newS,
|
||||
const std::vector<double>& newDists);
|
||||
|
||||
// Return the set of all Result objects
|
||||
const std::set<Result>& getResults() const;
|
||||
|
||||
// Print general stats to os
|
||||
void printStats(std::ostream* os) const;
|
||||
|
||||
// Print histogramgs for the results to os
|
||||
void printHisto(std::ostream* os, const std::set<Result>& result,
|
||||
const std::vector<double>& bins) const;
|
||||
|
||||
// Print a CSV for the results to os
|
||||
void printCsv(std::ostream* os, const std::set<Result>& result,
|
||||
const std::vector<double>& bins) const;
|
||||
|
||||
// Return the averaged average frechet distance
|
||||
double getAvgDist() const;
|
||||
|
||||
static FLine getWebMercLine(const Shape* s, double from, double to);
|
||||
static FLine getWebMercLine(const Shape* s, double from, double to,
|
||||
std::vector<double>* dists);
|
||||
|
||||
private:
|
||||
std::set<Result> _results;
|
||||
std::set<Result> _resultsAN;
|
||||
std::set<Result> _resultsAL;
|
||||
std::map<const Shape*, std::map<const Shape*, double> > _dCache;
|
||||
std::map<const Shape*, std::map<const Shape*, std::pair<size_t, double> > >
|
||||
_dACache;
|
||||
size_t _noOrigShp;
|
||||
size_t _noMatchShp;
|
||||
|
||||
double _fdSum;
|
||||
size_t _unmatchedSegSum;
|
||||
double _unmatchedSegLengthSum;
|
||||
|
||||
std::string _evalOutPath;
|
||||
|
||||
std::vector<double> _dfBins;
|
||||
|
||||
static std::pair<size_t, double> getDa(const std::vector<FLine>& a,
|
||||
const std::vector<FLine>& b);
|
||||
|
||||
static std::vector<FLine> segmentize(const Trip* t, const FLine& shape,
|
||||
const std::vector<double>& dists,
|
||||
const std::vector<double>* newTripDists);
|
||||
|
||||
static std::vector<double> getBins(double mind, double maxd, size_t steps);
|
||||
};
|
||||
|
||||
} // namespace eval
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_EVAL_COLLECTOR_H_
|
39
src/pfaedle/eval/Result.h
Normal file
39
src/pfaedle/eval/Result.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_EVAL_RESULT_H_
|
||||
#define PFAEDLE_EVAL_RESULT_H_
|
||||
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
using ad::cppgtfs::gtfs::Shape;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace eval {
|
||||
|
||||
/*
|
||||
* A single evaluation result.
|
||||
*/
|
||||
class Result {
|
||||
public:
|
||||
Result(const Trip* t, double dist) : _t(t), _dist(dist) {}
|
||||
|
||||
double getDist() const { return _dist; }
|
||||
const Trip* getTrip() const { return _t; }
|
||||
|
||||
private:
|
||||
const Trip* _t;
|
||||
double _dist;
|
||||
};
|
||||
|
||||
inline bool operator<(const Result& lhs, const Result& rhs) {
|
||||
return lhs.getDist() < rhs.getDist() ||
|
||||
(lhs.getDist() == rhs.getDist() && lhs.getTrip() < rhs.getTrip());
|
||||
}
|
||||
|
||||
} // namespace eval
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_EVAL_RESULT_H_
|
41
src/pfaedle/netgraph/EdgePL.h
Normal file
41
src/pfaedle/netgraph/EdgePL.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_NETGRAPH_EDGEPL_H_
|
||||
#define PFAEDLE_NETGRAPH_EDGEPL_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoEdgePL;
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace netgraph {
|
||||
|
||||
/*
|
||||
* A payload class for edges on a network graph - that is a graph
|
||||
* that exactly represents a physical public transit network
|
||||
*/
|
||||
class EdgePL : public GeoEdgePL<float> {
|
||||
public:
|
||||
EdgePL() {}
|
||||
EdgePL(const util::geo::FLine& l, const std::set<const Trip*>& trips)
|
||||
: _l(l), _trips(trips) {}
|
||||
const util::geo::FLine* getGeom() const { return &_l; }
|
||||
void getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["numtrips"] = std::to_string(_trips.size());
|
||||
}
|
||||
|
||||
private:
|
||||
util::geo::FLine _l;
|
||||
std::set<const Trip*> _trips;
|
||||
};
|
||||
} // namespace netgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_NETGRAPH_EDGEPL_H_
|
31
src/pfaedle/netgraph/Graph.h
Normal file
31
src/pfaedle/netgraph/Graph.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_NETGRAPH_GRAPH_H_
|
||||
#define PFAEDLE_NETGRAPH_GRAPH_H_
|
||||
|
||||
#include "pfaedle/netgraph/NodePL.h"
|
||||
#include "pfaedle/netgraph/EdgePL.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
|
||||
using util::geo::Point;
|
||||
using util::geo::Line;
|
||||
using util::geo::FPoint;
|
||||
using util::geo::FLine;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace netgraph {
|
||||
|
||||
/*
|
||||
* A payload class for edges on a network graph - that is a graph
|
||||
* that exactly represents a physical public transit network
|
||||
*/
|
||||
typedef util::graph::Edge<NodePL, EdgePL> Edge;
|
||||
typedef util::graph::Node<NodePL, EdgePL> Node;
|
||||
typedef util::graph::UndirGraph<NodePL, EdgePL> Graph;
|
||||
|
||||
} // namespace netgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_NETGRAPH_GRAPH_H_
|
38
src/pfaedle/netgraph/NodePL.h
Normal file
38
src/pfaedle/netgraph/NodePL.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_NETGRAPH_NODEPL_H_
|
||||
#define PFAEDLE_NETGRAPH_NODEPL_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoNodePL;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace netgraph {
|
||||
|
||||
/*
|
||||
* A payload class for edges on a network graph - that is a graph
|
||||
* that exactly represents a physical public transit network
|
||||
*/
|
||||
class NodePL : public GeoNodePL<float> {
|
||||
public:
|
||||
NodePL() {}
|
||||
NodePL(const util::geo::FPoint& geom) { _geom = geom; } // NOLINT
|
||||
|
||||
const util::geo::FPoint* getGeom() const { return &_geom; }
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) const {
|
||||
UNUSED(attrs);
|
||||
}
|
||||
|
||||
private:
|
||||
util::geo::FPoint _geom;
|
||||
};
|
||||
} // namespace netgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_NETGRAPH_NODEPL_H_
|
80
src/pfaedle/osm/BBoxIdx.cpp
Normal file
80
src/pfaedle/osm/BBoxIdx.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "pfaedle/osm/BBoxIdx.h"
|
||||
|
||||
using pfaedle::osm::BBoxIdx;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
BBoxIdx::BBoxIdx(float padding) : _padding(padding), _size(0) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void BBoxIdx::add(Box<float> box) {
|
||||
// division by 83.000m is only correct here around a latitude deg of 25,
|
||||
// but should be a good heuristic. 1 deg is around 63km at latitude deg of 44,
|
||||
// and 110 at deg=0, since we usually dont do map matching in the arctic,
|
||||
// its okay to use 83km here.
|
||||
box = util::geo::pad(box, _padding / 83000);
|
||||
addToTree(box, &_root, 0);
|
||||
_size++;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
size_t BBoxIdx::size() const { return _size; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool BBoxIdx::contains(const Point<float>& p) const {
|
||||
return treeHas(p, _root);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
util::geo::Box<float> BBoxIdx::getFullWebMercBox() const {
|
||||
return util::geo::FBox(
|
||||
util::geo::latLngToWebMerc<float>(_root.box.min_corner().get<1>(),
|
||||
_root.box.min_corner().get<0>()),
|
||||
util::geo::latLngToWebMerc<float>(_root.box.max_corner().get<1>(),
|
||||
_root.box.max_corner().get<0>()));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool BBoxIdx::treeHas(const Point<float>& p, const BBoxIdxNd& nd) const {
|
||||
if (!nd.childs.size()) return util::geo::contains(p, nd.box);
|
||||
for (const auto& child : nd.childs) {
|
||||
if (util::geo::contains(p, child.box)) return treeHas(p, child);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void BBoxIdx::addToTree(const Box<float>& box, BBoxIdxNd* nd, size_t lvl) {
|
||||
double bestCommonArea = 0;
|
||||
ssize_t bestChild = -1;
|
||||
|
||||
// 1. update the bbox of this node
|
||||
nd->box = util::geo::extendBox(box, nd->box);
|
||||
|
||||
if (lvl == MAX_LVL) return;
|
||||
|
||||
// 2. find best candidate
|
||||
for (size_t i = 0; i < nd->childs.size(); i++) {
|
||||
double cur = util::geo::commonArea(box, nd->childs[i].box);
|
||||
if (cur > MIN_COM_AREA && cur > bestCommonArea) {
|
||||
bestChild = i;
|
||||
bestCommonArea = cur;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestChild < 0) {
|
||||
// 3. add a new node with the inserted bbox
|
||||
nd->childs.push_back(BBoxIdxNd(box));
|
||||
addToTree(box, &nd->childs.back(), lvl + 1);
|
||||
} else {
|
||||
// 3. add to best node
|
||||
addToTree(box, &nd->childs[bestChild], lvl + 1);
|
||||
}
|
||||
|
||||
// TODO(patrick): some tree balancing by mergin overlapping bboxes in
|
||||
// non-leafs
|
||||
}
|
58
src/pfaedle/osm/BBoxIdx.h
Normal file
58
src/pfaedle/osm/BBoxIdx.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_OSM_BBOXIDX_H_
|
||||
#define PFAEDLE_OSM_BBOXIDX_H_
|
||||
|
||||
#include <vector>
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
using util::geo::Box;
|
||||
using util::geo::Point;
|
||||
|
||||
struct BBoxIdxNd {
|
||||
BBoxIdxNd() : box(util::geo::minbox<float>()) {}
|
||||
explicit BBoxIdxNd(const Box<float>& box) : box(box) {}
|
||||
Box<float> box;
|
||||
std::vector<BBoxIdxNd> childs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Poor man's R-tree
|
||||
*/
|
||||
class BBoxIdx {
|
||||
public:
|
||||
explicit BBoxIdx(float padding);
|
||||
|
||||
// Add a bounding box to this index
|
||||
void add(Box<float> box);
|
||||
|
||||
// Check if a point is contained in this index
|
||||
bool contains(const Point<float>& box) const;
|
||||
|
||||
// Return the full total bounding box of this index
|
||||
util::geo::Box<float> getFullWebMercBox() const;
|
||||
|
||||
// Return the size of this index
|
||||
size_t size() const;
|
||||
|
||||
private:
|
||||
double _padding;
|
||||
size_t _size;
|
||||
|
||||
BBoxIdxNd _root;
|
||||
|
||||
void addToTree(const Box<float>& box, BBoxIdxNd* nd, size_t lvl);
|
||||
bool treeHas(const Point<float>& p, const BBoxIdxNd& nd) const;
|
||||
|
||||
static const size_t MAX_LVL = 5;
|
||||
static constexpr double MIN_COM_AREA = 0.0;
|
||||
};
|
||||
} // namespace osm
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_OSM_BBOXIDX_H_
|
70
src/pfaedle/osm/Osm.h
Normal file
70
src/pfaedle/osm/Osm.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_OSM_OSM_H_
|
||||
#define PFAEDLE_OSM_OSM_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
typedef uint64_t osmid;
|
||||
|
||||
typedef std::unordered_map<std::string, std::string> AttrMap;
|
||||
typedef std::pair<std::string, std::string> Attr;
|
||||
typedef std::vector<osmid> OsmIdList;
|
||||
|
||||
struct OsmRel {
|
||||
OsmRel() : id(0) {}
|
||||
osmid id;
|
||||
AttrMap attrs;
|
||||
std::vector<osmid> nodes;
|
||||
std::vector<osmid> ways;
|
||||
|
||||
std::vector<std::string> nodeRoles;
|
||||
std::vector<std::string> wayRoles;
|
||||
|
||||
uint64_t keepFlags;
|
||||
uint64_t dropFlags;
|
||||
};
|
||||
|
||||
struct OsmWay {
|
||||
OsmWay() : id(0) {}
|
||||
osmid id;
|
||||
AttrMap attrs;
|
||||
std::vector<osmid> nodes;
|
||||
|
||||
uint64_t keepFlags;
|
||||
uint64_t dropFlags;
|
||||
};
|
||||
|
||||
struct OsmNode {
|
||||
OsmNode() : id(0) {}
|
||||
osmid id;
|
||||
double lat;
|
||||
double lng;
|
||||
AttrMap attrs;
|
||||
|
||||
uint64_t keepFlags;
|
||||
uint64_t dropFlags;
|
||||
};
|
||||
|
||||
struct Restriction {
|
||||
osmid eFrom, eTo;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<osmid, std::vector<Restriction>> RestrMap;
|
||||
|
||||
struct Restrictions {
|
||||
RestrMap pos;
|
||||
RestrMap neg;
|
||||
};
|
||||
} // namespace osm
|
||||
} // namespace pfaedle
|
||||
#endif // PFAEDLE_OSM_OSM_H_
|
1711
src/pfaedle/osm/OsmBuilder.cpp
Normal file
1711
src/pfaedle/osm/OsmBuilder.cpp
Normal file
File diff suppressed because it is too large
Load diff
252
src/pfaedle/osm/OsmBuilder.h
Normal file
252
src/pfaedle/osm/OsmBuilder.h
Normal file
|
@ -0,0 +1,252 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_OSM_OSMBUILDER_H_
|
||||
#define PFAEDLE_OSM_OSMBUILDER_H_
|
||||
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/osm/BBoxIdx.h"
|
||||
#include "pfaedle/osm/OsmFilter.h"
|
||||
#include "pfaedle/osm/OsmIdSet.h"
|
||||
#include "pfaedle/osm/OsmReadOpts.h"
|
||||
#include "pfaedle/osm/Restrictor.h"
|
||||
#include "pfaedle/router/Router.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "pfaedle/trgraph/Normalizer.h"
|
||||
#include "pfaedle/trgraph/StatInfo.h"
|
||||
#include "util/Nullable.h"
|
||||
#include "util/xml/XmlWriter.h"
|
||||
#include "xml/File.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
using pfaedle::trgraph::EdgeGrid;
|
||||
using pfaedle::trgraph::NodeGrid;
|
||||
using pfaedle::trgraph::Normalizer;
|
||||
using pfaedle::trgraph::Graph;
|
||||
using pfaedle::trgraph::Node;
|
||||
using pfaedle::trgraph::NodePL;
|
||||
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 {
|
||||
double dist;
|
||||
Node* node;
|
||||
const Edge* fromEdge;
|
||||
int fullTurns;
|
||||
};
|
||||
|
||||
struct SearchFunc {
|
||||
virtual bool operator()(const Node* n, const StatInfo* si) const = 0;
|
||||
};
|
||||
|
||||
struct EqSearch : public SearchFunc {
|
||||
double minSimi = 0.9;
|
||||
bool operator()(const Node* cand, const StatInfo* si) const {
|
||||
return cand->pl().getSI() && cand->pl().getSI()->simi(si) > minSimi;
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockSearch : public SearchFunc {
|
||||
bool operator()(const Node* n, const StatInfo* si) const {
|
||||
if (n->pl().getSI() && n->pl().getSI()->simi(si) < 0.5) return true;
|
||||
return n->pl().isBlocker();
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(const NodeCand& a, const NodeCand& b) {
|
||||
return a.fullTurns > b.fullTurns || a.dist > b.dist;
|
||||
}
|
||||
|
||||
typedef std::priority_queue<NodeCand> NodeCandPQ;
|
||||
|
||||
/*
|
||||
* Builds a physical transit network graph from OSM data
|
||||
*/
|
||||
class OsmBuilder {
|
||||
public:
|
||||
OsmBuilder();
|
||||
|
||||
// Read the OSM file at path, and write a graph to g. Only elements
|
||||
// inside the bounding box will be read
|
||||
void read(const std::string& path, const OsmReadOpts& opts, Graph* g,
|
||||
const BBoxIdx& box, size_t gridSize, router::FeedStops* fs,
|
||||
Restrictor* res);
|
||||
|
||||
|
||||
// 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
|
||||
void filterWrite(const std::string& in, const std::string& out,
|
||||
const std::vector<OsmReadOpts>& opts, const BBoxIdx& box);
|
||||
|
||||
private:
|
||||
xml::ParserState readBBoxNds(xml::File* xml, OsmIdSet* nodes,
|
||||
OsmIdSet* noHupNodes, const OsmFilter& filter,
|
||||
const BBoxIdx& bbox) const;
|
||||
|
||||
void readRels(xml::File* f, RelLst* rels, RelMap* nodeRels, RelMap* wayRels,
|
||||
const OsmFilter& filter, const AttrKeySet& keepAttrs,
|
||||
Restrictions* rests) const;
|
||||
|
||||
void readRestr(const OsmRel& rel, Restrictions* rests,
|
||||
const OsmFilter& filter) const;
|
||||
|
||||
void readNodes(xml::File* f, Graph* g, const RelLst& rels,
|
||||
const RelMap& nodeRels, const OsmFilter& filter,
|
||||
const OsmIdSet& bBoxNodes, NIdMap* nodes,
|
||||
NIdMultMap* multNodes, NodeSet* orphanStations,
|
||||
const AttrKeySet& keepAttrs, const FlatRels& flatRels,
|
||||
const OsmReadOpts& opts) const;
|
||||
|
||||
void readWriteNds(xml::File* i, util::xml::XmlWriter* o,
|
||||
const RelMap& nodeRels, const OsmFilter& filter,
|
||||
const OsmIdSet& bBoxNodes, NIdMap* nodes,
|
||||
const AttrKeySet& keepAttrs, const FlatRels& f) const;
|
||||
|
||||
void readWriteWays(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways,
|
||||
const AttrKeySet& keepAttrs) const;
|
||||
|
||||
void readWriteRels(xml::File* i, util::xml::XmlWriter* o, OsmIdList* ways,
|
||||
NIdMap* nodes, const OsmFilter& filter,
|
||||
const AttrKeySet& keepAttrs);
|
||||
|
||||
void readEdges(xml::File* xml, Graph* g, const RelLst& rels,
|
||||
const RelMap& wayRels, const OsmFilter& filter,
|
||||
const OsmIdSet& bBoxNodes, NIdMap* nodes,
|
||||
NIdMultMap* multNodes, const OsmIdSet& noHupNodes,
|
||||
const AttrKeySet& keepAttrs, const Restrictions& rest,
|
||||
Restrictor* restor, const FlatRels& flatRels,
|
||||
EdgTracks* etracks, const OsmReadOpts& opts);
|
||||
|
||||
void readEdges(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter,
|
||||
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
|
||||
OsmIdList* ret, NIdMap* nodes, const FlatRels& flatRels);
|
||||
|
||||
OsmWay nextWay(xml::File* xml, const RelMap& wayRels, const OsmFilter& filter,
|
||||
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
|
||||
const FlatRels& flatRels) const;
|
||||
|
||||
bool keepWay(const OsmWay& w, const RelMap& wayRels, const OsmFilter& filter,
|
||||
const OsmIdSet& bBoxNodes, const FlatRels& fl) const;
|
||||
|
||||
OsmWay nextWayWithId(xml::File* xml, osmid wid,
|
||||
const AttrKeySet& keepAttrs) const;
|
||||
|
||||
OsmNode nextNode(xml::File* xml, NIdMap* nodes, NIdMultMap* multNodes,
|
||||
const RelMap& nodeRels, const OsmFilter& filter,
|
||||
const OsmIdSet& bBoxNodes, const AttrKeySet& keepAttrs,
|
||||
const FlatRels& flatRels) const;
|
||||
|
||||
bool keepNode(const OsmNode& n, const NIdMap& nodes,
|
||||
const NIdMultMap& multNodes, const RelMap& nodeRels,
|
||||
const OsmIdSet& bBoxNodes, const OsmFilter& filter,
|
||||
const FlatRels& fl) const;
|
||||
|
||||
OsmRel nextRel(xml::File* xml, const OsmFilter& filter,
|
||||
const AttrKeySet& keepAttrs) const;
|
||||
|
||||
Nullable<StatInfo> getStatInfo(Node* node, osmid nid, const FPoint& pos,
|
||||
const AttrMap& m, StAttrGroups* groups,
|
||||
const RelMap& nodeRels, const RelLst& rels,
|
||||
const OsmReadOpts& ops) const;
|
||||
|
||||
void writeGeoms(Graph* g) const;
|
||||
void deleteOrphNds(Graph* g) const;
|
||||
void deleteOrphEdgs(Graph* g) const;
|
||||
double dist(const Node* a, const Node* b) const;
|
||||
double webMercDist(const Node* a, const Node* b) const;
|
||||
double webMercDistFactor(const FPoint& a) const;
|
||||
|
||||
NodeGrid buildNodeIdx(Graph* g, size_t size,
|
||||
const util::geo::Box<float>& webMercBox,
|
||||
bool which) const;
|
||||
|
||||
EdgeGrid buildEdgeIdx(Graph* g, size_t size,
|
||||
const util::geo::Box<float>& webMercBox) const;
|
||||
|
||||
void fixGaps(Graph* g, NodeGrid* ng) const;
|
||||
void collapseEdges(Graph* g) const;
|
||||
void writeODirEdgs(Graph* g, Restrictor* restor) const;
|
||||
void writeSelfEdgs(Graph* g) const;
|
||||
void writeEdgeTracks(const EdgTracks& tracks) const;
|
||||
void simplifyGeoms(Graph* g) const;
|
||||
uint32_t writeComps(Graph* g) const;
|
||||
bool edgesSim(const Edge* a, const Edge* b) const;
|
||||
const EdgePL& mergeEdgePL(Edge* a, Edge* b) const;
|
||||
void getEdgCands(const FPoint& s, EdgeCandPQ* ret, EdgeGrid* eg,
|
||||
double d) const;
|
||||
|
||||
std::set<Node*> getMatchingNds(const NodePL& s, NodeGrid* ng, double d) const;
|
||||
|
||||
Node* getMatchingNd(const NodePL& s, NodeGrid* ng, double d) const;
|
||||
|
||||
NodeSet snapStation(Graph* g, NodePL* s, EdgeGrid* eg, NodeGrid* sng,
|
||||
const OsmReadOpts& opts, Restrictor* restor, bool surHeur,
|
||||
double maxD) const;
|
||||
|
||||
// Checks if from the edge e, a station similar to si can be reach with less
|
||||
// than maxD distance and less or equal to "maxFullTurns" full turns. If
|
||||
// such a station exists, it is returned. If not, 0 is returned.
|
||||
Node* eqStatReach(const Edge* e, const StatInfo* si, const FPoint& p,
|
||||
double maxD, int maxFullTurns, double maxAng) const;
|
||||
|
||||
Node* depthSearch(const Edge* e, const StatInfo* si,
|
||||
const util::geo::FPoint& p, double maxD, int maxFullTurns,
|
||||
double minAngle, const SearchFunc& sfunc) const;
|
||||
|
||||
bool isBlocked(const Edge* e, const StatInfo* si, const FPoint& p,
|
||||
double maxD, int maxFullTurns, double minAngle) const;
|
||||
|
||||
StatGroup* groupStats(const NodeSet& s) const;
|
||||
|
||||
std::vector<TransitEdgeLine*> getLines(const std::vector<size_t>& edgeRels,
|
||||
const RelLst& rels,
|
||||
const OsmReadOpts& ops);
|
||||
|
||||
NodePL plFromGtfs(const Stop* s, const OsmReadOpts& ops) const;
|
||||
|
||||
void getKeptAttrKeys(const OsmReadOpts& opts, AttrKeySet sets[3]) const;
|
||||
|
||||
void skipUntil(xml::File* xml, const std::string& s) const;
|
||||
|
||||
void processRestr(osmid nid, osmid wid, const Restrictions& rawRests, Edge* e,
|
||||
Node* n, Restrictor* restor) const;
|
||||
|
||||
std::string getAttrByFirstMatch(const DeepAttrLst& rule, osmid id,
|
||||
const AttrMap& attrs, const RelMap& entRels,
|
||||
const RelLst& rels,
|
||||
const Normalizer& norm) const;
|
||||
std::vector<std::string> getAttrMatchRanked(const DeepAttrLst& rule, osmid id,
|
||||
const AttrMap& attrs,
|
||||
const RelMap& entRels,
|
||||
const RelLst& rels,
|
||||
const Normalizer& norm) const;
|
||||
|
||||
std::string getAttr(const DeepAttrRule& s, osmid id, const AttrMap& attrs,
|
||||
const RelMap& entRels, const RelLst& rels) const;
|
||||
|
||||
bool relKeep(osmid id, const RelMap& rels, const FlatRels& fl) const;
|
||||
|
||||
std::map<TransitEdgeLine, TransitEdgeLine*> _lines;
|
||||
std::map<size_t, TransitEdgeLine*> _relLines;
|
||||
};
|
||||
} // namespace osm
|
||||
} // namespace pfaedle
|
||||
#endif // PFAEDLE_OSM_OSMBUILDER_H_
|
260
src/pfaedle/osm/OsmFilter.cpp
Normal file
260
src/pfaedle/osm/OsmFilter.cpp
Normal file
|
@ -0,0 +1,260 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "pfaedle/osm/OsmFilter.h"
|
||||
|
||||
using pfaedle::osm::OsmFilter;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
OsmFilter::OsmFilter(const MultAttrMap& keep, const MultAttrMap& drop)
|
||||
: _keep(keep), _drop(drop) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
OsmFilter::OsmFilter(const OsmReadOpts& o)
|
||||
: _keep(o.keepFilter),
|
||||
_drop(o.dropFilter),
|
||||
_nohup(o.noHupFilter),
|
||||
_oneway(o.oneWayFilter),
|
||||
_onewayrev(o.oneWayFilterRev),
|
||||
_twoway(o.twoWayFilter),
|
||||
_station(o.stationFilter),
|
||||
_blocker(o.stationBlockerFilter),
|
||||
_posRestr(o.restrPosRestr),
|
||||
_negRestr(o.restrNegRestr),
|
||||
_noRestr(o.noRestrFilter),
|
||||
_levels(o.levelFilters) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::keep(const AttrMap& attrs, Type t) const {
|
||||
return contained(attrs, _keep, t);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::drop(const AttrMap& attrs, Type t) const {
|
||||
return contained(attrs, _drop, t);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::nohup(const char* key, const char* v) const {
|
||||
const auto& dkv = _nohup.find(key);
|
||||
if (dkv != _nohup.end()) {
|
||||
for (const auto& val : dkv->second) {
|
||||
if (valMatches(v, val.first)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::oneway(const AttrMap& attrs) const {
|
||||
if (contained(attrs, _twoway, WAY)) return false;
|
||||
return contained(attrs, _oneway, WAY);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::onewayrev(const AttrMap& attrs) const {
|
||||
if (contained(attrs, _twoway, WAY)) return false;
|
||||
return contained(attrs, _onewayrev, WAY);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::station(const AttrMap& attrs) const {
|
||||
return contained(attrs, _station, NODE);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::blocker(const AttrMap& attrs) const {
|
||||
return contained(attrs, _blocker, NODE);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::contained(const AttrMap& attrs, const MultAttrMap& map,
|
||||
Type t) {
|
||||
for (const auto& kv : attrs) {
|
||||
const auto& dkv = map.find(kv.first);
|
||||
|
||||
if (dkv != map.end()) {
|
||||
for (const auto& val : dkv->second) {
|
||||
bool multValMatch = val.second & osm::MULT_VAL_MATCH;
|
||||
if (val.second & t) continue;
|
||||
if (valMatches(kv.second, val.first, multValMatch)) return val.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::contained(const AttrMap& attrs, const Attr& attr) {
|
||||
for (const auto& kv : attrs) {
|
||||
if (kv.first == attr.first) return valMatches(kv.second, attr.second);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint8_t OsmFilter::level(const AttrMap& attrs) const {
|
||||
// the best matching level is always returned
|
||||
for (int16_t i = 0; i < 7; i++) {
|
||||
for (const auto& kv : attrs) {
|
||||
const auto& lkv = (_levels + i)->find(kv.first);
|
||||
if (lkv != (_levels + i)->end()) {
|
||||
for (const auto& val : lkv->second) {
|
||||
if (valMatches(kv.second, val.first)) return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool OsmFilter::valMatches(const std::string& a, const std::string& b) {
|
||||
return valMatches(a, b, false);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool OsmFilter::valMatches(const std::string& a, const std::string& b, bool m) {
|
||||
if (b == "*") return true;
|
||||
|
||||
if (m) {
|
||||
// search for occurances in semicolon separated list
|
||||
if (a.find(std::string(";") + b) != std::string::npos) return true;
|
||||
if (a.find(b + ";") != std::string::npos) return true;
|
||||
if (a.find(std::string("; ") + b) != std::string::npos) return true;
|
||||
if (a.find(b + " ;") != std::string::npos) return true;
|
||||
}
|
||||
|
||||
return a == b;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::vector<std::string> OsmFilter::getAttrKeys() const {
|
||||
std::vector<std::string> ret;
|
||||
for (const auto& kv : _keep) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _drop) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _nohup) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _oneway) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _twoway) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _station) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _blocker) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _posRestr) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _negRestr) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (const auto& kv : _noRestr) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
for (uint8_t i = 0; i < 7; i++) {
|
||||
for (const auto& kv : *(_levels + i)) {
|
||||
ret.push_back(kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
OsmFilter OsmFilter::merge(const OsmFilter& other) const {
|
||||
MultAttrMap keep;
|
||||
MultAttrMap drop;
|
||||
|
||||
for (const auto& kv : _keep) {
|
||||
keep[kv.first].insert(kv.second.begin(), kv.second.end());
|
||||
}
|
||||
|
||||
for (const auto& kv : other._keep) {
|
||||
keep[kv.first].insert(kv.second.begin(), kv.second.end());
|
||||
}
|
||||
|
||||
// TODO(patrick): multi-level combination for filters. otherwise
|
||||
// filter drop filters meant as a refinement for keep filters
|
||||
// interfere with other keeps
|
||||
|
||||
// const auto* d = &_drop;
|
||||
|
||||
// for (size_t i = 0; i < 2; i++) {
|
||||
// for (const auto& kv : *d) {
|
||||
// if (keep.find(kv.first) != keep.end()) {
|
||||
// for (const auto& val : kv.second) {
|
||||
// if (keep[kv.first].find(val.first) == keep[kv.first].end()) {
|
||||
// drop[kv.first].insert(val);
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// drop[kv.first].insert(kv.second.begin(), kv.second.end());
|
||||
// }
|
||||
// }
|
||||
// d = &other._drop;
|
||||
// }
|
||||
|
||||
return OsmFilter(keep, drop);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string OsmFilter::toString() const {
|
||||
std::stringstream ss;
|
||||
ss << "[KEEP]\n\n";
|
||||
|
||||
for (const auto& kv : _keep) {
|
||||
ss << " " << kv.first << "=";
|
||||
bool first = false;
|
||||
for (const auto& v : kv.second) {
|
||||
if (first) ss << ",";
|
||||
first = true;
|
||||
ss << v.first;
|
||||
}
|
||||
ss << "\n";
|
||||
}
|
||||
|
||||
ss << "\n[DROP]\n\n";
|
||||
|
||||
for (const auto& kv : _drop) {
|
||||
ss << " " << kv.first << "=";
|
||||
bool first = false;
|
||||
for (const auto& v : kv.second) {
|
||||
if (first) ss << ",";
|
||||
first = true;
|
||||
ss << v.first;
|
||||
}
|
||||
ss << "\n";
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::negRestr(const AttrMap& attrs) const {
|
||||
if (contained(attrs, _noRestr, ALL)) return false;
|
||||
return (contained(attrs, _negRestr, ALL));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint64_t OsmFilter::posRestr(const AttrMap& attrs) const {
|
||||
if (contained(attrs, _noRestr, ALL)) return false;
|
||||
return (contained(attrs, _posRestr, ALL));
|
||||
}
|
51
src/pfaedle/osm/OsmFilter.h
Normal file
51
src/pfaedle/osm/OsmFilter.h
Normal 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_OSM_OSMFILTER_H_
|
||||
#define PFAEDLE_OSM_OSMFILTER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "pfaedle/osm/Osm.h"
|
||||
#include "pfaedle/osm/OsmReadOpts.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
class OsmFilter {
|
||||
public:
|
||||
enum Type : uint64_t { NODE = 16, WAY = 8, REL = 4, ALL = 0 };
|
||||
OsmFilter() {}
|
||||
OsmFilter(const MultAttrMap& keep, const MultAttrMap& drop);
|
||||
explicit OsmFilter(const OsmReadOpts& o);
|
||||
uint64_t keep(const AttrMap& attrs, Type t) const;
|
||||
uint64_t drop(const AttrMap& attrs, Type t) const;
|
||||
uint64_t nohup(const char* key, const char* val) const;
|
||||
uint8_t level(const AttrMap& attrs) const;
|
||||
uint64_t oneway(const AttrMap& attrs) const;
|
||||
uint64_t onewayrev(const AttrMap& attrs) const;
|
||||
uint64_t station(const AttrMap& attrs) const;
|
||||
uint64_t blocker(const AttrMap& attrs) const;
|
||||
uint64_t negRestr(const AttrMap& attrs) const;
|
||||
uint64_t posRestr(const AttrMap& attrs) const;
|
||||
std::vector<std::string> getAttrKeys() const;
|
||||
|
||||
OsmFilter merge(const OsmFilter& other) const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
static bool valMatches(const std::string& a, const std::string& b, bool m);
|
||||
static bool valMatches(const std::string& a, const std::string& b);
|
||||
static uint64_t contained(const AttrMap& attrs, const MultAttrMap& map,
|
||||
Type t);
|
||||
static uint64_t contained(const AttrMap& attrs, const Attr& map);
|
||||
|
||||
private:
|
||||
MultAttrMap _keep, _drop, _nohup, _oneway, _onewayrev, _twoway, _station,
|
||||
_blocker, _posRestr, _negRestr, _noRestr;
|
||||
const MultAttrMap* _levels;
|
||||
};
|
||||
} // namespace osm
|
||||
} // namespace pfaedle
|
||||
#endif // PFAEDLE_OSM_OSMFILTER_H_
|
302
src/pfaedle/osm/OsmIdSet.cpp
Normal file
302
src/pfaedle/osm/OsmIdSet.cpp
Normal file
|
@ -0,0 +1,302 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "pfaedle/osm/OsmIdSet.h"
|
||||
|
||||
using pfaedle::osm::OsmIdSet;
|
||||
|
||||
size_t OsmIdSet::LOOKUPS = 0;
|
||||
size_t OsmIdSet::FLOOKUPS = 0;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
OsmIdSet::OsmIdSet()
|
||||
: _closed(false),
|
||||
_sorted(true),
|
||||
_last(0),
|
||||
_smallest(-1),
|
||||
_biggest(0),
|
||||
_obufpos(0),
|
||||
_curBlock(-1),
|
||||
_fsize(0) {
|
||||
_bitset = new std::bitset<BLOOMF_BITS>();
|
||||
_file = openTmpFile();
|
||||
|
||||
_buffer = new unsigned char[BUFFER_S];
|
||||
_outBuffer = new unsigned char[OBUFFER_S];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
OsmIdSet::~OsmIdSet() {
|
||||
delete _bitset;
|
||||
delete[] _buffer;
|
||||
if (!_closed) delete[] _outBuffer;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void OsmIdSet::add(osmid id) {
|
||||
if (_closed) throw std::exception();
|
||||
diskAdd(id);
|
||||
// _set.insert(id);
|
||||
|
||||
if (_last > id) _sorted = false;
|
||||
_last = id;
|
||||
if (id < _smallest) _smallest = id;
|
||||
if (id > _biggest) _biggest = id;
|
||||
|
||||
for (int i = 0; i < 10; i++) (*_bitset)[hash(id, i)] = 1;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void OsmIdSet::diskAdd(osmid id) {
|
||||
memcpy(_outBuffer + _obufpos, &id, 8);
|
||||
|
||||
_obufpos += 8;
|
||||
|
||||
if (_obufpos % BUFFER_S == 0) {
|
||||
// this is the last value in this block
|
||||
_blockEnds.push_back(id);
|
||||
}
|
||||
|
||||
if (_obufpos >= OBUFFER_S) {
|
||||
ssize_t w = cwrite(_file, _outBuffer, OBUFFER_S);
|
||||
_fsize += w;
|
||||
_obufpos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
size_t OsmIdSet::getBlock(osmid id) const {
|
||||
auto it = std::upper_bound(_blockEnds.begin(), _blockEnds.end(), id);
|
||||
return (it - _blockEnds.begin());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool OsmIdSet::diskHas(osmid id) const {
|
||||
assert(_sorted);
|
||||
|
||||
if (std::find(_blockEnds.begin(), _blockEnds.end(), id) != _blockEnds.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t block = getBlock(id);
|
||||
|
||||
if (block != _curBlock) {
|
||||
lseek(_file, block * BUFFER_S, SEEK_SET);
|
||||
|
||||
ssize_t n = cread(_file, _buffer, BUFFER_S);
|
||||
_curBlockSize = n;
|
||||
FLOOKUPS++;
|
||||
_curBlock = block;
|
||||
}
|
||||
|
||||
if (_curBlockSize <= 7) return false;
|
||||
if (*(reinterpret_cast<uint64_t*>(_buffer)) > id) return false;
|
||||
|
||||
ssize_t l = 0;
|
||||
ssize_t r = _curBlockSize - 8;
|
||||
|
||||
while (l <= r) {
|
||||
unsigned char* p = _buffer + (l + ((r - l) / 16) * 8);
|
||||
osmid cur = *(reinterpret_cast<uint64_t*>(p));
|
||||
if (cur == id) return true;
|
||||
if (cur < id)
|
||||
l = (p - _buffer) + 8;
|
||||
else
|
||||
r = (p - _buffer) - 8;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool OsmIdSet::has(osmid id) const {
|
||||
LOOKUPS++;
|
||||
if (!_closed) close();
|
||||
|
||||
if (id < _smallest || id > _biggest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if ((*_bitset)[hash(id, i)] == 0) return false;
|
||||
}
|
||||
|
||||
bool has = diskHas(id);
|
||||
// assert(has == (bool)_set.count(id));
|
||||
return has;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void OsmIdSet::close() const {
|
||||
ssize_t w = cwrite(_file, _outBuffer, _obufpos);
|
||||
_fsize += w;
|
||||
_blockEnds.push_back(_biggest);
|
||||
delete[] _outBuffer;
|
||||
_closed = true;
|
||||
|
||||
// if order was not sorted, sort now
|
||||
if (!_sorted) sort();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void OsmIdSet::sort() const {
|
||||
// sort file via an external merge sort
|
||||
|
||||
_blockEnds.clear();
|
||||
size_t parts = _fsize / SORT_BUFFER_S + 1;
|
||||
size_t partsBufSize = ((SORT_BUFFER_S / 8) / parts + 1) * 8;
|
||||
|
||||
unsigned char* buf = new unsigned char[SORT_BUFFER_S];
|
||||
unsigned char** partbufs = new unsigned char*[parts];
|
||||
size_t* partpos = new size_t[parts];
|
||||
size_t* partsize = new size_t[parts];
|
||||
|
||||
// sort the 'parts' number of file parts independently
|
||||
for (size_t i = 0; i < parts; i++) {
|
||||
partbufs[i] = new unsigned char[partsBufSize];
|
||||
partpos[i] = 0;
|
||||
partsize[i] = 0;
|
||||
lseek(_file, SORT_BUFFER_S * i, SEEK_SET);
|
||||
ssize_t n = read(_file, buf, SORT_BUFFER_S);
|
||||
if (n < 0) continue;
|
||||
qsort(buf, n / 8, 8, qsortCmp);
|
||||
lseek(_file, SORT_BUFFER_S * i, SEEK_SET);
|
||||
cwrite(_file, buf, n);
|
||||
|
||||
memcpy(partbufs[i], buf, std::min<size_t>(n, partsBufSize));
|
||||
partsize[i] = n;
|
||||
}
|
||||
|
||||
// now the individial parts are sorted
|
||||
int newFile = openTmpFile();
|
||||
|
||||
for (size_t i = 0; i < _fsize; i += 8) {
|
||||
uint64_t smallest = UINT64_MAX;
|
||||
ssize_t smallestP = -1;
|
||||
|
||||
// look for smallest element (not optimal, but running time is not
|
||||
// really critical here)
|
||||
for (size_t j = 0; j < parts; j++) {
|
||||
if (partpos[j] == partsize[j]) continue; // bucket already empty
|
||||
if (*reinterpret_cast<uint64_t*>(
|
||||
&partbufs[j][partpos[j] % partsBufSize]) <= smallest) {
|
||||
smallestP = j;
|
||||
smallest = *reinterpret_cast<uint64_t*>(
|
||||
&partbufs[j][partpos[j] % partsBufSize]);
|
||||
}
|
||||
}
|
||||
|
||||
assert(smallestP > -1);
|
||||
|
||||
memcpy(buf + (i % SORT_BUFFER_S), &smallest, 8);
|
||||
|
||||
if ((i + 8) % BUFFER_S == 0) _blockEnds.push_back(smallest);
|
||||
|
||||
if ((i % SORT_BUFFER_S) == SORT_BUFFER_S - 8 || i == _fsize - 8) {
|
||||
// write to output file
|
||||
cwrite(newFile, buf, i % SORT_BUFFER_S + 8);
|
||||
}
|
||||
|
||||
partpos[smallestP] += 8;
|
||||
|
||||
if (partpos[smallestP] % partsBufSize == 0) {
|
||||
lseek(_file, SORT_BUFFER_S * smallestP + partpos[smallestP], SEEK_SET);
|
||||
cread(_file, partbufs[smallestP], partsBufSize);
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
delete[] buf;
|
||||
for (size_t j = 0; j < parts; j++) delete[] partbufs[j];
|
||||
delete[] partbufs;
|
||||
delete[] partpos;
|
||||
delete[] partsize;
|
||||
|
||||
_file = newFile;
|
||||
_sorted = true;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
size_t OsmIdSet::cwrite(int f, const void* buf, size_t n) const {
|
||||
ssize_t w = write(f, buf, n);
|
||||
if (w < 0) {
|
||||
throw std::runtime_error("OSMIDSET: could not write to tmp file.\n");
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
size_t OsmIdSet::cread(int f, void* buf, size_t n) const {
|
||||
ssize_t w = read(f, buf, n);
|
||||
if (w < 0) {
|
||||
throw std::runtime_error("OSMIDSET: could not read from tmp file.\n");
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint32_t OsmIdSet::knuth(uint32_t in) const {
|
||||
const uint32_t prime = 2654435769;
|
||||
return (in * prime) >> 2;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint32_t OsmIdSet::jenkins(uint32_t in) const {
|
||||
in = (in + 0x7ed55d16) + (in << 12);
|
||||
in = (in ^ 0xc761c23c) ^ (in >> 19);
|
||||
in = (in + 0x165667b1) + (in << 5);
|
||||
in = (in + 0xd3a2646c) ^ (in << 9);
|
||||
in = (in + 0xfd7046c5) + (in << 3);
|
||||
in = (in ^ 0xb55a4f09) ^ (in >> 16);
|
||||
return in >> 2;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint32_t OsmIdSet::hash(uint32_t in, int i) const {
|
||||
return (knuth(in) + jenkins(in) * i) % BLOOMF_BITS;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
int OsmIdSet::openTmpFile() const {
|
||||
const std::string& fname = getFName();
|
||||
int file = open(fname.c_str(), O_RDWR | O_CREAT, 0666);
|
||||
|
||||
// immediately unlink
|
||||
unlink(fname.c_str());
|
||||
|
||||
if (file < 0) {
|
||||
std::cerr << "Could not open temporary file " << fname << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
posix_fadvise(file, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
return file;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string OsmIdSet::getFName() const {
|
||||
std::string f = ".pfaedle-tmp";
|
||||
|
||||
while (access(f.c_str(), F_OK) != -1) {
|
||||
std::stringstream ss;
|
||||
ss << ".pfaedle-tmp-";
|
||||
ss << std::rand();
|
||||
f = ss.str().c_str();
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
91
src/pfaedle/osm/OsmIdSet.h
Normal file
91
src/pfaedle/osm/OsmIdSet.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_OSM_OSMIDSET_H_
|
||||
#define PFAEDLE_OSM_OSMIDSET_H_
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <bitset>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "pfaedle/osm/Osm.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
// buffer sizes _must_ be multiples of 8
|
||||
static const size_t BUFFER_S = 8 * 64 * 1024;
|
||||
static const size_t SORT_BUFFER_S = 8 * 64 * 1024;
|
||||
static const size_t OBUFFER_S = 8 * 1024 * 1024;
|
||||
|
||||
#define BLOOMF_BITS 400000000
|
||||
|
||||
/*
|
||||
* A disk-based set for OSM ids. Read-access for checking the presence is
|
||||
* reduced by a bloom filter
|
||||
*/
|
||||
class OsmIdSet {
|
||||
public:
|
||||
OsmIdSet();
|
||||
~OsmIdSet();
|
||||
|
||||
// Add an OSM id
|
||||
void add(osmid id);
|
||||
|
||||
// Check if an OSM id is contained
|
||||
bool has(osmid id) const;
|
||||
|
||||
// Count the number of lookups and file lookups for debugging
|
||||
static size_t LOOKUPS;
|
||||
static size_t FLOOKUPS;
|
||||
|
||||
private:
|
||||
std::set<osmid> _set;
|
||||
mutable bool _closed;
|
||||
mutable int _file;
|
||||
unsigned char* _buffer;
|
||||
unsigned char* _outBuffer;
|
||||
mutable bool _sorted;
|
||||
osmid _last;
|
||||
osmid _smallest;
|
||||
osmid _biggest;
|
||||
|
||||
size_t _obufpos;
|
||||
mutable size_t _curBlock;
|
||||
mutable ssize_t _curBlockSize;
|
||||
|
||||
// bloom filter
|
||||
std::bitset<BLOOMF_BITS>* _bitset;
|
||||
|
||||
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;
|
||||
bool diskHas(osmid id) const;
|
||||
std::string getFName() const;
|
||||
size_t getBlock(osmid id) const;
|
||||
int openTmpFile() const;
|
||||
size_t cwrite(int f, const void* buf, size_t n) const;
|
||||
size_t cread(int f, void* buf, size_t n) const;
|
||||
|
||||
static int qsortCmp(const void* a, const void* b) {
|
||||
if (*static_cast<const uint64_t*>(a) < *static_cast<const uint64_t*>(b))
|
||||
return -1;
|
||||
if (*static_cast<const uint64_t*>(a) > *static_cast<const uint64_t*>(b))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
} // namespace osm
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_OSM_OSMIDSET_H_
|
197
src/pfaedle/osm/OsmReadOpts.h
Normal file
197
src/pfaedle/osm/OsmReadOpts.h
Normal file
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_OSM_OSMREADOPTS_H_
|
||||
#define PFAEDLE_OSM_OSMREADOPTS_H_
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include "pfaedle/osm/Osm.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "pfaedle/trgraph/Normalizer.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
typedef std::unordered_set<std::string> AttrKeySet;
|
||||
typedef std::unordered_map<osmid, trgraph::Node*> NIdMap;
|
||||
typedef std::unordered_map<osmid, std::set<trgraph::Node*>> NIdMultMap;
|
||||
typedef std::pair<double, trgraph::Edge*> EdgeCand;
|
||||
typedef std::priority_queue<EdgeCand> EdgeCandPQ;
|
||||
typedef std::unordered_map<osmid, std::vector<size_t>> RelMap;
|
||||
typedef std::vector<AttrMap> RelVec;
|
||||
typedef std::vector<std::string> AttrLst;
|
||||
|
||||
typedef std::pair<std::string, uint64_t> AttrFlagPair;
|
||||
typedef std::unordered_map<std::string, std::map<std::string, uint64_t>>
|
||||
MultAttrMap;
|
||||
|
||||
typedef std::pair<std::string, std::string> KeyVal;
|
||||
typedef std::set<size_t> FlatRels;
|
||||
|
||||
typedef std::unordered_map<const trgraph::Edge*, std::string> EdgTracks;
|
||||
|
||||
struct RelLst {
|
||||
RelVec rels;
|
||||
FlatRels flat;
|
||||
};
|
||||
|
||||
enum FilterFlags : uint64_t {
|
||||
USE = 1, // dummy value
|
||||
REL_NO_DOWN = 2,
|
||||
NO_RELATIONS = 4,
|
||||
NO_WAYS = 8,
|
||||
NO_NODES = 16,
|
||||
MULT_VAL_MATCH = 32
|
||||
};
|
||||
|
||||
struct FilterRule {
|
||||
FilterRule() : kv(KeyVal("", "")) {}
|
||||
KeyVal kv;
|
||||
std::set<std::string> flags;
|
||||
};
|
||||
|
||||
inline bool operator==(const FilterRule& a, const FilterRule& b) {
|
||||
return a.kv == b.kv && a.flags == b.flags;
|
||||
}
|
||||
|
||||
struct DeepAttrRule {
|
||||
std::string attr;
|
||||
FilterRule relRule;
|
||||
};
|
||||
|
||||
inline bool operator==(const DeepAttrRule& a, const DeepAttrRule& b) {
|
||||
return a.attr == b.attr && a.relRule == b.relRule;
|
||||
}
|
||||
|
||||
typedef std::vector<DeepAttrRule> DeepAttrLst;
|
||||
|
||||
struct RelLineRules {
|
||||
AttrLst sNameRule;
|
||||
AttrLst fromNameRule;
|
||||
AttrLst toNameRule;
|
||||
};
|
||||
|
||||
inline bool operator==(const RelLineRules& a, const RelLineRules& b) {
|
||||
return a.sNameRule == b.sNameRule && a.fromNameRule == b.fromNameRule &&
|
||||
a.toNameRule == b.toNameRule;
|
||||
}
|
||||
|
||||
struct StationAttrRules {
|
||||
DeepAttrLst nameRule;
|
||||
DeepAttrLst platformRule;
|
||||
};
|
||||
|
||||
inline bool operator==(const StationAttrRules& a, const StationAttrRules& b) {
|
||||
return a.nameRule == b.nameRule && a.platformRule == b.platformRule;
|
||||
}
|
||||
|
||||
struct StatGroupNAttrRule {
|
||||
DeepAttrRule attr;
|
||||
double maxDist;
|
||||
};
|
||||
|
||||
inline bool operator==(const StatGroupNAttrRule& a,
|
||||
const StatGroupNAttrRule& b) {
|
||||
return a.attr == b.attr && a.maxDist == b.maxDist;
|
||||
}
|
||||
|
||||
typedef std::unordered_map<
|
||||
std::string,
|
||||
std::unordered_map<std::string, std::vector<trgraph::StatGroup*>>>
|
||||
StAttrGroups;
|
||||
|
||||
struct OsmReadOpts {
|
||||
OsmReadOpts() {}
|
||||
|
||||
MultAttrMap noHupFilter;
|
||||
MultAttrMap keepFilter;
|
||||
MultAttrMap levelFilters[7];
|
||||
MultAttrMap dropFilter;
|
||||
MultAttrMap oneWayFilter;
|
||||
MultAttrMap oneWayFilterRev;
|
||||
MultAttrMap twoWayFilter;
|
||||
MultAttrMap stationFilter;
|
||||
MultAttrMap stationBlockerFilter;
|
||||
std::vector<StatGroupNAttrRule> statGroupNAttrRules;
|
||||
|
||||
trgraph::Normalizer statNormzer;
|
||||
trgraph::Normalizer lineNormzer;
|
||||
trgraph::Normalizer trackNormzer;
|
||||
|
||||
RelLineRules relLinerules;
|
||||
StationAttrRules statAttrRules;
|
||||
|
||||
DeepAttrLst edgePlatformRules;
|
||||
|
||||
uint8_t maxSnapLevel;
|
||||
|
||||
double maxAngleSnapReach;
|
||||
std::vector<double> maxSnapDistances;
|
||||
double maxSnapFallbackHeurDistance;
|
||||
double maxGroupSearchDistance;
|
||||
double maxBlockDistance;
|
||||
|
||||
double maxOsmStationDistance;
|
||||
|
||||
// TODO(patrick): this is not implemented yet
|
||||
double levelSnapPunishFac[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
// restriction system
|
||||
MultAttrMap restrPosRestr;
|
||||
MultAttrMap restrNegRestr;
|
||||
MultAttrMap noRestrFilter;
|
||||
};
|
||||
|
||||
inline bool operator==(const OsmReadOpts& a, const OsmReadOpts& b) {
|
||||
if (a.maxSnapDistances.size() != b.maxSnapDistances.size()) return false;
|
||||
for (size_t i = 0; i < a.maxSnapDistances.size(); i++) {
|
||||
if (fabs(a.maxSnapDistances[i] - b.maxSnapDistances[i]) >= 0.1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.noHupFilter == b.noHupFilter && a.keepFilter == b.keepFilter &&
|
||||
a.levelFilters[0] == b.levelFilters[0] &&
|
||||
a.levelFilters[1] == b.levelFilters[1] &&
|
||||
a.levelFilters[2] == b.levelFilters[2] &&
|
||||
a.levelFilters[3] == b.levelFilters[3] &&
|
||||
a.levelFilters[4] == b.levelFilters[4] &&
|
||||
a.levelFilters[5] == b.levelFilters[5] &&
|
||||
a.levelFilters[6] == b.levelFilters[6] &&
|
||||
a.dropFilter == b.dropFilter && a.oneWayFilter == b.oneWayFilter &&
|
||||
a.oneWayFilterRev == b.oneWayFilterRev &&
|
||||
a.twoWayFilter == b.twoWayFilter &&
|
||||
a.stationFilter == b.stationFilter &&
|
||||
a.stationBlockerFilter == b.stationBlockerFilter &&
|
||||
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.maxOsmStationDistance - b.maxOsmStationDistance) < 0.1 &&
|
||||
fabs(a.maxSnapFallbackHeurDistance - b.maxSnapFallbackHeurDistance) <
|
||||
0.1 &&
|
||||
fabs(a.maxGroupSearchDistance - b.maxGroupSearchDistance) < 0.1 &&
|
||||
fabs(a.maxBlockDistance - b.maxBlockDistance) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[0] - b.levelSnapPunishFac[0]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[1] - b.levelSnapPunishFac[1]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[2] - b.levelSnapPunishFac[2]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[3] - b.levelSnapPunishFac[3]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[4] - b.levelSnapPunishFac[4]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[5] - b.levelSnapPunishFac[5]) < 0.1 &&
|
||||
fabs(a.levelSnapPunishFac[6] - b.levelSnapPunishFac[6]) < 0.1 &&
|
||||
a.restrPosRestr == b.restrPosRestr &&
|
||||
a.restrNegRestr == b.restrNegRestr &&
|
||||
a.noRestrFilter == b.noRestrFilter;
|
||||
}
|
||||
} // namespace osm
|
||||
} // namespace pfaedle
|
||||
#endif // PFAEDLE_OSM_OSMREADOPTS_H_
|
155
src/pfaedle/osm/Restrictor.cpp
Normal file
155
src/pfaedle/osm/Restrictor.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "pfaedle/osm/Restrictor.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
using pfaedle::osm::Restrictor;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Restrictor::relax(osmid wid, const trgraph::Node* n,
|
||||
const trgraph::Edge* e) {
|
||||
// the wid is not unique here, because the OSM ways are split into
|
||||
// multiple edges. They are only unique as a pair with a "from-via" point.
|
||||
_rlx[NodeOsmIdP(n, wid)] = e;
|
||||
auto i = _posDangling.find(NodeOsmIdP(n, wid));
|
||||
if (i != _posDangling.end()) {
|
||||
for (const auto& path : i->second) {
|
||||
_pos[path.first][path.second].second = e;
|
||||
assert(path.first->hasEdge(e));
|
||||
}
|
||||
}
|
||||
|
||||
auto j = _negDangling.find(NodeOsmIdP(n, wid));
|
||||
if (j != _negDangling.end()) {
|
||||
for (const auto& path : j->second) {
|
||||
_neg[path.first][path.second].second = e;
|
||||
assert(path.first->hasEdge(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Restrictor::add(const trgraph::Edge* from, osmid to,
|
||||
const trgraph::Node* via, bool pos) {
|
||||
const trgraph::Edge* toE = 0;
|
||||
if (_rlx.count(NodeOsmIdP(via, to)))
|
||||
toE = _rlx.find(NodeOsmIdP(via, to))->second;
|
||||
if (pos) {
|
||||
_pos[via].push_back(RulePair(from, toE));
|
||||
if (!toE)
|
||||
_posDangling[NodeOsmIdP(via, to)].push_back(
|
||||
DanglPath(via, _pos[via].size() - 1));
|
||||
} else {
|
||||
_neg[via].push_back(RulePair(from, toE));
|
||||
if (!toE)
|
||||
_negDangling[NodeOsmIdP(via, to)].push_back(
|
||||
DanglPath(via, _neg[via].size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool Restrictor::may(const trgraph::Edge* from, const trgraph::Edge* to,
|
||||
const trgraph::Node* via) const {
|
||||
auto posI = _pos.find(via);
|
||||
auto negI = _neg.find(via);
|
||||
|
||||
if (posI != _pos.end()) {
|
||||
for (const auto& r : posI->second) {
|
||||
if (r.first == from && r.second && r.second != to)
|
||||
return false;
|
||||
else if (r.first == from && r.second == to)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (negI != _neg.end()) {
|
||||
for (const auto& r : negI->second) {
|
||||
if (r.first == from && r.second == to) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Restrictor::replaceEdge(const trgraph::Edge* old,
|
||||
const trgraph::Edge* newA,
|
||||
const trgraph::Edge* newB) {
|
||||
const trgraph::Edge* newFrom;
|
||||
const trgraph::Edge* newTo;
|
||||
if (old->getFrom() == newA->getFrom() || old->getFrom() == newA->getTo()) {
|
||||
newFrom = newA;
|
||||
newTo = newB;
|
||||
} else {
|
||||
newFrom = newB;
|
||||
newTo = newA;
|
||||
}
|
||||
replaceEdge(old, old->getFrom(), newFrom);
|
||||
replaceEdge(old, old->getTo(), newTo);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Restrictor::duplicateEdge(const trgraph::Edge* old,
|
||||
const trgraph::Edge* newE) {
|
||||
duplicateEdge(old, old->getFrom(), newE);
|
||||
duplicateEdge(old, old->getTo(), newE);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Restrictor::duplicateEdge(const trgraph::Edge* old,
|
||||
const trgraph::Node* via,
|
||||
const trgraph::Edge* newE) {
|
||||
auto posI = _pos.find(via);
|
||||
auto negI = _neg.find(via);
|
||||
|
||||
assert(old->getFrom() == newE->getTo() && old->getTo() == newE->getFrom());
|
||||
|
||||
if (posI != _pos.end()) {
|
||||
for (auto& r : posI->second) {
|
||||
if (r.first == old) {
|
||||
if (r.first->getTo() != via) {
|
||||
r.first = newE;
|
||||
}
|
||||
}
|
||||
if (r.second == old) {
|
||||
if (r.second->getFrom() != via) {
|
||||
r.second = newE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (negI != _neg.end()) {
|
||||
for (auto& r : negI->second) {
|
||||
if (r.first == old) {
|
||||
if (r.first->getTo() != via) {
|
||||
r.first = newE;
|
||||
}
|
||||
}
|
||||
if (r.second == old) {
|
||||
if (r.second->getFrom() != via) {
|
||||
r.second = newE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Restrictor::replaceEdge(const trgraph::Edge* old, const trgraph::Node* via,
|
||||
const trgraph::Edge* newE) {
|
||||
auto posI = _pos.find(via);
|
||||
auto negI = _neg.find(via);
|
||||
|
||||
if (posI != _pos.end()) {
|
||||
for (auto& r : posI->second) {
|
||||
if (r.first == old) r.first = newE;
|
||||
if (r.second == old) r.second = newE;
|
||||
}
|
||||
}
|
||||
if (negI != _neg.end()) {
|
||||
for (auto& r : negI->second) {
|
||||
if (r.first == old) r.first = newE;
|
||||
if (r.second == old) r.second = newE;
|
||||
}
|
||||
}
|
||||
}
|
60
src/pfaedle/osm/Restrictor.h
Normal file
60
src/pfaedle/osm/Restrictor.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_OSM_RESTRICTOR_H_
|
||||
#define PFAEDLE_OSM_RESTRICTOR_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include "pfaedle/osm/Osm.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace osm {
|
||||
|
||||
typedef std::pair<const trgraph::Edge*, const trgraph::Edge*> RulePair;
|
||||
typedef std::vector<RulePair> RuleVec;
|
||||
typedef std::pair<const trgraph::Node*, size_t> DanglPath;
|
||||
// very seldom, there are more than a handful of rules for a node. Use a
|
||||
// vector here, should have lesser overhead and be faster for such small
|
||||
// numbers
|
||||
typedef std::unordered_map<const trgraph::Node*, RuleVec> Rules;
|
||||
typedef std::pair<const trgraph::Node*, osmid> NodeOsmIdP;
|
||||
|
||||
/*
|
||||
* Stores restrictions between edges
|
||||
*/
|
||||
class Restrictor {
|
||||
public:
|
||||
Restrictor() {}
|
||||
|
||||
void relax(osmid wid, const trgraph::Node* n, const trgraph::Edge* e);
|
||||
void add(const trgraph::Edge* from, osmid to, const trgraph::Node* via,
|
||||
bool pos);
|
||||
bool may(const trgraph::Edge* from, const trgraph::Edge* to,
|
||||
const trgraph::Node* via) const;
|
||||
void replaceEdge(const trgraph::Edge* old, const trgraph::Edge* newA,
|
||||
const trgraph::Edge* newB);
|
||||
void duplicateEdge(const trgraph::Edge* old, const trgraph::Node* via,
|
||||
const trgraph::Edge* newE);
|
||||
void duplicateEdge(const trgraph::Edge* old, const trgraph::Edge* newE);
|
||||
|
||||
private:
|
||||
Rules _pos;
|
||||
Rules _neg;
|
||||
|
||||
std::map<NodeOsmIdP, const trgraph::Edge*> _rlx;
|
||||
|
||||
std::map<NodeOsmIdP, std::vector<DanglPath>> _posDangling;
|
||||
std::map<NodeOsmIdP, std::vector<DanglPath>> _negDangling;
|
||||
|
||||
void replaceEdge(const trgraph::Edge* old, const trgraph::Node* via,
|
||||
const trgraph::Edge* newE);
|
||||
};
|
||||
} // namespace osm
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_OSM_RESTRICTOR_H_
|
75
src/pfaedle/router/Comp.h
Normal file
75
src/pfaedle/router/Comp.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_COMP_H_
|
||||
#define PFAEDLE_ROUTER_COMP_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "util/String.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
using util::editDist;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double statSimi(const std::string& a, const std::string& b) {
|
||||
if (a == b) return 1;
|
||||
|
||||
if (a.empty() || b.empty()) return 0;
|
||||
|
||||
if (a.size() > b.size() + 1) {
|
||||
// check if a begins with b
|
||||
if (a.compare(0, b.size() + 1, b + " ") == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if a ends with b
|
||||
if (a.compare(a.size() - (b.size() + 1), b.size() + 1, " " + b) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (b.size() > a.size() + 1) {
|
||||
// check if b begins with a
|
||||
if (b.compare(0, a.size() + 1, a + " ") == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if b ends with a
|
||||
if (b.compare(b.size() - (a.size() + 1), a.size() + 1, " " + a) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<double>(editDist(a, b)) /
|
||||
(std::max(static_cast<double>(a.size()),
|
||||
static_cast<double>(b.size()))) <
|
||||
0.05)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double lineSimi(const std::string& a, const std::string& b) {
|
||||
if (a == b) return 1;
|
||||
|
||||
if (a.empty() || b.empty()) return 0;
|
||||
|
||||
// if one of the lines is completely contained in the other, return 1
|
||||
if (a.find(b) != std::string::npos) {
|
||||
return 1;
|
||||
} else if (b.find(a) != std::string::npos) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_COMP_H_
|
99
src/pfaedle/router/EdgePL.cpp
Normal file
99
src/pfaedle/router/EdgePL.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "pfaedle/router/EdgePL.h"
|
||||
#include "pfaedle/router/Router.h"
|
||||
#include "util/String.h"
|
||||
|
||||
using pfaedle::router::EdgePL;
|
||||
using pfaedle::router::EdgeCost;
|
||||
using pfaedle::router::EdgeList;
|
||||
using pfaedle::trgraph::Node;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeList* EdgePL::getEdges() { return &_edges; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const EdgeList& EdgePL::getEdges() const { return _edges; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const FPoint& EdgePL::frontHop() const {
|
||||
if (!_edges.size()) return *_end->pl().getGeom();
|
||||
return _edges.back()->pl().frontHop();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const FPoint& EdgePL::backHop() const {
|
||||
if (!_edges.size()) return *_start->pl().getGeom();
|
||||
return _edges.front()->pl().backHop();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const Node* EdgePL::backNode() const { return _end; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const Node* EdgePL::frontNode() const { return _start; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FLine* EdgePL::getGeom() const {
|
||||
if (!_edges.size()) return 0;
|
||||
if (!_geom.size()) {
|
||||
const trgraph::Node* l = _start;
|
||||
for (auto i = _edges.rbegin(); i != _edges.rend(); i++) {
|
||||
const auto e = *i;
|
||||
if ((e->getFrom() == l) ^ e->pl().isRev()) {
|
||||
_geom.insert(_geom.end(), e->pl().getGeom()->begin(),
|
||||
e->pl().getGeom()->end());
|
||||
} else {
|
||||
_geom.insert(_geom.end(), e->pl().getGeom()->rbegin(),
|
||||
e->pl().getGeom()->rend());
|
||||
}
|
||||
l = e->getOtherNd(l);
|
||||
}
|
||||
}
|
||||
|
||||
return &_geom;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setStartNode(const trgraph::Node* s) { _start = s; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setEndNode(const trgraph::Node* e) { _end = e; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setStartEdge(const trgraph::Edge* s) { _startE = s; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setEndEdge(const trgraph::Edge* e) { _endE = e; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const EdgeCost& EdgePL::getCost() const { return _cost; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setCost(const router::EdgeCost& c) { _cost = c; }
|
||||
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["cost"] = std::to_string(_cost.getValue());
|
||||
(*obj)["from_edge"] = util::toString(_startE);
|
||||
(*obj)["to_edge"] = util::toString(_endE);
|
||||
(*obj)["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
|
||||
(*obj)["cost_m_lvl0"] = std::to_string(_cost.meterDist);
|
||||
(*obj)["cost_m_lvl1"] = std::to_string(_cost.meterDistLvl1);
|
||||
(*obj)["cost_m_lvl2"] = std::to_string(_cost.meterDistLvl2);
|
||||
(*obj)["cost_m_lvl3"] = std::to_string(_cost.meterDistLvl3);
|
||||
(*obj)["cost_m_lvl4"] = std::to_string(_cost.meterDistLvl4);
|
||||
(*obj)["cost_m_lvl5"] = std::to_string(_cost.meterDistLvl5);
|
||||
(*obj)["cost_m_lvl6"] = std::to_string(_cost.meterDistLvl6);
|
||||
(*obj)["cost_m_lvl7"] = std::to_string(_cost.meterDistLvl7);
|
||||
(*obj)["cost_fullturn"] = std::to_string(_cost.fullTurns);
|
||||
(*obj)["cost_st_passthru"] = std::to_string(_cost.passThruStations);
|
||||
(*obj)["cost_m_oneway"] = std::to_string(_cost.oneWayMeters);
|
||||
(*obj)["cost_m_lineunmatch"] = std::to_string(_cost.lineUnmatchedMeters);
|
||||
(*obj)["cost_reach_node_pen"] = std::to_string(_cost.reachPen);
|
||||
(*obj)["cost_oneway_event"] = std::to_string(_cost.oneWayEdges);
|
||||
(*obj)["dummy"] = _edges.size() ? "no" : "yes";
|
||||
}
|
49
src/pfaedle/router/EdgePL.h
Normal file
49
src/pfaedle/router/EdgePL.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_EDGEPL_H_
|
||||
#define PFAEDLE_ROUTER_EDGEPL_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "pfaedle/router/Misc.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoEdgePL;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
class EdgePL : public GeoEdgePL<float> {
|
||||
public:
|
||||
EdgePL() : _cost(), _start(0), _end(0), _startE(0), _endE(0) {}
|
||||
const util::geo::FLine* getGeom() const;
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) const;
|
||||
router::EdgeList* getEdges();
|
||||
const router::EdgeList& getEdges() const;
|
||||
void setStartNode(const trgraph::Node* s);
|
||||
void setEndNode(const trgraph::Node* s);
|
||||
void setStartEdge(const trgraph::Edge* s);
|
||||
void setEndEdge(const trgraph::Edge* s);
|
||||
const router::EdgeCost& getCost() const;
|
||||
void setCost(const router::EdgeCost& c);
|
||||
const FPoint& frontHop() const;
|
||||
const FPoint& backHop() const;
|
||||
const trgraph::Node* frontNode() const;
|
||||
const trgraph::Node* backNode() const;
|
||||
|
||||
private:
|
||||
router::EdgeCost _cost;
|
||||
// the edges are in this field in REVERSED ORDER!
|
||||
router::EdgeList _edges;
|
||||
const trgraph::Node* _start;
|
||||
const trgraph::Node* _end;
|
||||
const trgraph::Edge* _startE;
|
||||
const trgraph::Edge* _endE;
|
||||
mutable util::geo::FLine _geom;
|
||||
};
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_EDGEPL_H_
|
26
src/pfaedle/router/Graph.h
Normal file
26
src/pfaedle/router/Graph.h
Normal 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_
|
197
src/pfaedle/router/Misc.h
Normal file
197
src/pfaedle/router/Misc.h
Normal file
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_MISC_H_
|
||||
#define PFAEDLE_ROUTER_MISC_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "ad/cppgtfs/gtfs/Route.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
|
||||
using ad::cppgtfs::gtfs::Route;
|
||||
using ad::cppgtfs::gtfs::Stop;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
struct NodeCand {
|
||||
trgraph::Node* nd;
|
||||
double pen;
|
||||
};
|
||||
|
||||
struct RoutingOpts {
|
||||
RoutingOpts()
|
||||
: fullTurnPunishFac(2000),
|
||||
fullTurnAngle(45),
|
||||
passThruStationsPunish(100),
|
||||
oneWayPunishFac(1),
|
||||
oneWayEdgePunish(0),
|
||||
lineUnmatchedPunishFact(0.5),
|
||||
platformUnmatchedPen(0),
|
||||
stationDistPenFactor(0) {}
|
||||
double fullTurnPunishFac;
|
||||
double fullTurnAngle;
|
||||
double passThruStationsPunish;
|
||||
double oneWayPunishFac;
|
||||
double oneWayEdgePunish;
|
||||
double lineUnmatchedPunishFact;
|
||||
double platformUnmatchedPen;
|
||||
double stationDistPenFactor;
|
||||
double nonOsmPen;
|
||||
double levelPunish[8];
|
||||
};
|
||||
|
||||
inline bool operator==(const RoutingOpts& a, const RoutingOpts& b) {
|
||||
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.platformUnmatchedPen - b.platformUnmatchedPen) < 0.01 &&
|
||||
fabs(a.stationDistPenFactor - b.stationDistPenFactor) < 0.01 &&
|
||||
fabs(a.nonOsmPen - b.nonOsmPen) < 0.01 &&
|
||||
fabs(a.levelPunish[0] - b.levelPunish[0]) < 0.01 &&
|
||||
fabs(a.levelPunish[1] - b.levelPunish[1]) < 0.01 &&
|
||||
fabs(a.levelPunish[2] - b.levelPunish[2]) < 0.01 &&
|
||||
fabs(a.levelPunish[3] - b.levelPunish[3]) < 0.01 &&
|
||||
fabs(a.levelPunish[4] - b.levelPunish[4]) < 0.01 &&
|
||||
fabs(a.levelPunish[5] - b.levelPunish[5]) < 0.01 &&
|
||||
fabs(a.levelPunish[6] - b.levelPunish[6]) < 0.01 &&
|
||||
fabs(a.levelPunish[7] - b.levelPunish[7]) < 0.01;
|
||||
}
|
||||
|
||||
struct EdgeCost {
|
||||
EdgeCost()
|
||||
: meterDist(0),
|
||||
meterDistLvl1(0),
|
||||
meterDistLvl2(0),
|
||||
meterDistLvl3(0),
|
||||
meterDistLvl4(0),
|
||||
meterDistLvl5(0),
|
||||
meterDistLvl6(0),
|
||||
meterDistLvl7(0),
|
||||
fullTurns(0),
|
||||
passThruStations(0),
|
||||
oneWayMeters(0),
|
||||
oneWayEdges(0),
|
||||
lineUnmatchedMeters(0),
|
||||
reachPen(0),
|
||||
o(0) {}
|
||||
EdgeCost(double mDist, double mDistLvl1, double mDistLvl2, double mDistLvl3,
|
||||
double mDistLvl4, double mDistLvl5, double mDistLvl6,
|
||||
double mDistLvl7, uint32_t fullTurns, int32_t passThru,
|
||||
double oneWayMeters, size_t oneWayEdges, double lineUnmatchedMeters,
|
||||
double reachPen, const RoutingOpts* o)
|
||||
: meterDist(mDist),
|
||||
meterDistLvl1(mDistLvl1),
|
||||
meterDistLvl2(mDistLvl2),
|
||||
meterDistLvl3(mDistLvl3),
|
||||
meterDistLvl4(mDistLvl4),
|
||||
meterDistLvl5(mDistLvl5),
|
||||
meterDistLvl6(mDistLvl6),
|
||||
meterDistLvl7(mDistLvl7),
|
||||
fullTurns(fullTurns),
|
||||
passThruStations(passThru),
|
||||
oneWayMeters(oneWayMeters),
|
||||
oneWayEdges(oneWayEdges),
|
||||
lineUnmatchedMeters(lineUnmatchedMeters),
|
||||
reachPen(reachPen),
|
||||
o(o) {}
|
||||
double meterDist;
|
||||
double meterDistLvl1;
|
||||
double meterDistLvl2;
|
||||
double meterDistLvl3;
|
||||
double meterDistLvl4;
|
||||
double meterDistLvl5;
|
||||
double meterDistLvl6;
|
||||
double meterDistLvl7;
|
||||
uint32_t fullTurns;
|
||||
int32_t passThruStations;
|
||||
double oneWayMeters;
|
||||
size_t oneWayEdges;
|
||||
double lineUnmatchedMeters;
|
||||
double reachPen;
|
||||
const RoutingOpts* o;
|
||||
|
||||
double getValue() const {
|
||||
if (!o) return meterDist + reachPen;
|
||||
return meterDist * o->levelPunish[0] + meterDistLvl1 * o->levelPunish[1] +
|
||||
meterDistLvl2 * o->levelPunish[2] +
|
||||
meterDistLvl3 * o->levelPunish[3] +
|
||||
meterDistLvl4 * o->levelPunish[4] +
|
||||
meterDistLvl5 * o->levelPunish[5] +
|
||||
meterDistLvl6 * o->levelPunish[6] +
|
||||
meterDistLvl7 * o->levelPunish[7] +
|
||||
oneWayMeters * o->oneWayPunishFac +
|
||||
oneWayEdges * o->oneWayEdgePunish +
|
||||
lineUnmatchedMeters * o->lineUnmatchedPunishFact +
|
||||
fullTurns * o->fullTurnPunishFac +
|
||||
passThruStations * o->passThruStationsPunish + reachPen;
|
||||
}
|
||||
|
||||
double getTotalMeters() const {
|
||||
return meterDist + meterDistLvl1 + meterDistLvl2 + meterDistLvl3 +
|
||||
meterDistLvl4 + meterDistLvl5 + meterDistLvl6 + meterDistLvl7;
|
||||
}
|
||||
};
|
||||
|
||||
inline EdgeCost operator+(const EdgeCost& a, const EdgeCost& b) {
|
||||
return EdgeCost(
|
||||
a.meterDist + b.meterDist, a.meterDistLvl1 + b.meterDistLvl1,
|
||||
a.meterDistLvl2 + b.meterDistLvl2, a.meterDistLvl3 + b.meterDistLvl3,
|
||||
a.meterDistLvl4 + b.meterDistLvl4, a.meterDistLvl5 + b.meterDistLvl5,
|
||||
a.meterDistLvl6 + b.meterDistLvl6, a.meterDistLvl7 + b.meterDistLvl7,
|
||||
a.fullTurns + b.fullTurns, a.passThruStations + b.passThruStations,
|
||||
a.oneWayMeters + b.oneWayMeters, a.oneWayEdges + b.oneWayEdges,
|
||||
a.lineUnmatchedMeters + b.lineUnmatchedMeters, a.reachPen + b.reachPen,
|
||||
a.o ? a.o : b.o);
|
||||
}
|
||||
|
||||
inline bool operator<=(const EdgeCost& a, const EdgeCost& b) {
|
||||
return a.getValue() <= b.getValue();
|
||||
}
|
||||
|
||||
inline bool operator==(const EdgeCost& a, const EdgeCost& b) {
|
||||
return a.getValue() == b.getValue();
|
||||
}
|
||||
|
||||
inline bool operator>(const EdgeCost& a, const EdgeCost& b) {
|
||||
return a.getValue() > b.getValue();
|
||||
}
|
||||
|
||||
|
||||
inline int angSmaller(const FPoint& f, const FPoint& m, const FPoint& t,
|
||||
double ang) {
|
||||
if (util::geo::innerProd(m, f, t) < ang) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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<trgraph::Edge*> EdgeList;
|
||||
typedef std::vector<trgraph::Node*> NodeList;
|
||||
|
||||
struct EdgeListHop {
|
||||
EdgeList edges;
|
||||
const trgraph::Node* start;
|
||||
const trgraph::Node* end;
|
||||
};
|
||||
|
||||
typedef std::vector<EdgeListHop> EdgeListHops;
|
||||
|
||||
typedef std::set<Route::TYPE> MOTs;
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_MISC_H_
|
36
src/pfaedle/router/NodePL.h
Normal file
36
src/pfaedle/router/NodePL.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_NODEPL_H_
|
||||
#define PFAEDLE_ROUTER_NODEPL_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoNodePL;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
class NodePL : public GeoNodePL<float> {
|
||||
public:
|
||||
NodePL() : _n(0) {}
|
||||
NodePL(const pfaedle::trgraph::Node* n) : _n(n) {} // NOLINT
|
||||
|
||||
const util::geo::FPoint* getGeom() const {
|
||||
return !_n ? 0 : _n->pl().getGeom();
|
||||
}
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) const {
|
||||
if (_n) _n->pl().getAttrs(attrs);
|
||||
}
|
||||
|
||||
private:
|
||||
const pfaedle::trgraph::Node* _n;
|
||||
};
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_NODEPL_H_
|
615
src/pfaedle/router/Router.cpp
Normal file
615
src/pfaedle/router/Router.cpp
Normal file
|
@ -0,0 +1,615 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <omp.h>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "pfaedle/router/Comp.h"
|
||||
#include "pfaedle/router/Router.h"
|
||||
#include "pfaedle/router/RoutingAttrs.h"
|
||||
#include "util/geo/output/GeoGraphJsonOutput.h"
|
||||
#include "util/graph/Dijkstra.h"
|
||||
#include "util/graph/EDijkstra.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
using pfaedle::router::Router;
|
||||
using pfaedle::router::EdgeCost;
|
||||
using pfaedle::router::CostFunc;
|
||||
using pfaedle::router::DistHeur;
|
||||
using pfaedle::router::NCostFunc;
|
||||
using pfaedle::router::NDistHeur;
|
||||
using pfaedle::router::CombCostFunc;
|
||||
using pfaedle::router::EdgeListHop;
|
||||
using pfaedle::router::EdgeListHops;
|
||||
using pfaedle::router::RoutingOpts;
|
||||
using pfaedle::router::RoutingAttrs;
|
||||
using pfaedle::router::HopBand;
|
||||
using pfaedle::router::NodeCandRoute;
|
||||
using util::graph::EDijkstra;
|
||||
using util::graph::Dijkstra;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeCost NCostFunc::operator()(const trgraph::Node* from,
|
||||
const trgraph::Edge* e,
|
||||
const trgraph::Node* to) const {
|
||||
UNUSED(to);
|
||||
if (!from) return EdgeCost();
|
||||
|
||||
int oneway = e->pl().oneWay() == 2;
|
||||
int32_t stationSkip = 0;
|
||||
|
||||
double transitLinePen = 0; // transitLineCmp(e->pl());
|
||||
|
||||
return EdgeCost(e->pl().lvl() == 0 ? e->pl().getLength() : 0,
|
||||
e->pl().lvl() == 1 ? e->pl().getLength() : 0,
|
||||
e->pl().lvl() == 2 ? e->pl().getLength() : 0,
|
||||
e->pl().lvl() == 3 ? e->pl().getLength() : 0,
|
||||
e->pl().lvl() == 4 ? e->pl().getLength() : 0,
|
||||
e->pl().lvl() == 5 ? e->pl().getLength() : 0,
|
||||
e->pl().lvl() == 6 ? e->pl().getLength() : 0,
|
||||
e->pl().lvl() == 7 ? e->pl().getLength() : 0, 0, stationSkip,
|
||||
e->pl().getLength() * oneway, oneway,
|
||||
e->pl().getLength() * transitLinePen, 0, &_rOpts);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeCost CostFunc::operator()(const trgraph::Edge* from, const trgraph::Node* n,
|
||||
const trgraph::Edge* to) const {
|
||||
if (!from) return EdgeCost();
|
||||
|
||||
uint32_t fullTurns = 0;
|
||||
int oneway = from->pl().oneWay() == 2;
|
||||
int32_t stationSkip = 0;
|
||||
|
||||
if (n) {
|
||||
if (from->getFrom() == to->getTo() && from->getTo() == to->getFrom()) {
|
||||
// trivial full turn
|
||||
fullTurns = 1;
|
||||
} else if (n->getDeg() > 2) {
|
||||
// otherwise, only intersection angles will be punished
|
||||
fullTurns = router::angSmaller(from->pl().backHop(), *n->pl().getGeom(),
|
||||
to->pl().frontHop(), _rOpts.fullTurnAngle);
|
||||
}
|
||||
|
||||
if (from->pl().isRestricted() && !_res.may(from, to, n)) oneway = 1;
|
||||
|
||||
// for debugging
|
||||
n->pl().setVisited();
|
||||
|
||||
if (_tgGrp && n->pl().getSI() && n->pl().getSI()->getGroup() != _tgGrp)
|
||||
stationSkip = 1;
|
||||
}
|
||||
|
||||
double transitLinePen = transitLineCmp(from->pl());
|
||||
|
||||
return EdgeCost(from->pl().lvl() == 0 ? from->pl().getLength() : 0,
|
||||
from->pl().lvl() == 1 ? from->pl().getLength() : 0,
|
||||
from->pl().lvl() == 2 ? from->pl().getLength() : 0,
|
||||
from->pl().lvl() == 3 ? from->pl().getLength() : 0,
|
||||
from->pl().lvl() == 4 ? from->pl().getLength() : 0,
|
||||
from->pl().lvl() == 5 ? from->pl().getLength() : 0,
|
||||
from->pl().lvl() == 6 ? from->pl().getLength() : 0,
|
||||
from->pl().lvl() == 7 ? from->pl().getLength() : 0, fullTurns,
|
||||
stationSkip, from->pl().getLength() * oneway, oneway,
|
||||
from->pl().getLength() * transitLinePen, 0, &_rOpts);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double CostFunc::transitLineCmp(const trgraph::EdgePL& e) const {
|
||||
double best = 1;
|
||||
for (const auto* l : e.getLines()) {
|
||||
double cur = _rAttrs.simi(l);
|
||||
|
||||
if (cur < 0.0001) return cur;
|
||||
if (cur < best) best = cur;
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NDistHeur::NDistHeur(const RoutingOpts& rOpts,
|
||||
const std::set<trgraph::Node*>& tos)
|
||||
: _rOpts(rOpts), _maxCentD(0) {
|
||||
size_t c = 0;
|
||||
double x = 0, y = 0;
|
||||
for (auto to : tos) {
|
||||
x += to->pl().getGeom()->get<0>();
|
||||
y += to->pl().getGeom()->get<1>();
|
||||
c++;
|
||||
}
|
||||
|
||||
x /= c;
|
||||
y /= c;
|
||||
_center = FPoint(x, y);
|
||||
|
||||
for (auto to : tos) {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::webMercMeterDist(*to->pl().getGeom(), _center)));
|
||||
if (cur > _maxCentD) _maxCentD = cur;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
DistHeur::DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
|
||||
const std::set<trgraph::Edge*>& tos)
|
||||
: _rOpts(rOpts), _lvl(minLvl), _maxCentD(0) {
|
||||
size_t c = 0;
|
||||
double x = 0, y = 0;
|
||||
for (auto to : tos) {
|
||||
x += to->getFrom()->pl().getGeom()->get<0>();
|
||||
y += to->getFrom()->pl().getGeom()->get<1>();
|
||||
c++;
|
||||
}
|
||||
|
||||
x /= c;
|
||||
y /= c;
|
||||
_center = FPoint(x, y);
|
||||
|
||||
for (auto to : tos) {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::webMercMeterDist(*to->getFrom()->pl().getGeom(), _center) *
|
||||
_rOpts.levelPunish[_lvl]));
|
||||
if (cur > _maxCentD) _maxCentD = cur;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeCost DistHeur::operator()(const trgraph::Edge* a,
|
||||
const std::set<trgraph::Edge*>& b) const {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::webMercMeterDist(*a->getTo()->pl().getGeom(), _center) *
|
||||
_rOpts.levelPunish[_lvl]));
|
||||
|
||||
UNUSED(b);
|
||||
|
||||
return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeCost NDistHeur::operator()(const trgraph::Node* a,
|
||||
const std::set<trgraph::Node*>& b) const {
|
||||
double cur = static_cast<double>(static_cast<double>(
|
||||
util::geo::webMercMeterDist(*a->pl().getGeom(), _center)));
|
||||
|
||||
UNUSED(b);
|
||||
|
||||
return EdgeCost(cur - _maxCentD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double CombCostFunc::operator()(const router::Edge* from, const router::Node* n,
|
||||
const router::Edge* to) const {
|
||||
UNUSED(n);
|
||||
UNUSED(from);
|
||||
return to->pl().getCost().getValue();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
Router::Router(const trgraph::Graph& g, size_t numThreads)
|
||||
: _g(g), _cache(numThreads) {
|
||||
for (size_t i = 0; i < numThreads; i++) {
|
||||
_cache[i] = new Cache();
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
Router::~Router() {
|
||||
for (size_t i = 0; i < _cache.size(); i++) {
|
||||
delete _cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool Router::compConned(const NodeCandGroup& a, const NodeCandGroup& b) const {
|
||||
for (auto n1 : a) {
|
||||
for (auto n2 : b) {
|
||||
if (n1.nd->pl().getComp() == n2.nd->pl().getComp()) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
HopBand Router::getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const {
|
||||
double pend = 0;
|
||||
for (size_t i = 0; i < a.size(); i++) {
|
||||
for (size_t j = 0; j < b.size(); j++) {
|
||||
double d = util::geo::webMercMeterDist(*a[i].nd->pl().getGeom(),
|
||||
*b[j].nd->pl().getGeom());
|
||||
if (d > pend) pend = d;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(VDEBUG) << "Pending max hop distance is " << pend << " meters";
|
||||
|
||||
const trgraph::StatGroup* tgGrpTo = 0;
|
||||
if (b.begin()->nd->pl().getSI())
|
||||
tgGrpTo = b.begin()->nd->pl().getSI()->getGroup();
|
||||
|
||||
CostFunc costF(rAttrs, rOpts, rest, tgGrpTo, pend * 50);
|
||||
|
||||
std::set<trgraph::Edge *> from, to;
|
||||
|
||||
// TODO(patrick): test if the two sets share a common connected component
|
||||
|
||||
for (auto n : a)
|
||||
from.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end());
|
||||
|
||||
for (auto n : b)
|
||||
to.insert(n.nd->getAdjListOut().begin(), n.nd->getAdjListOut().end());
|
||||
|
||||
LOG(VDEBUG) << "Doing pilot run between " << from.size() << "->" << to.size()
|
||||
<< " candidates";
|
||||
|
||||
EdgeList el;
|
||||
EdgeCost ret = costF.inf();
|
||||
DistHeur distH(0, rOpts, to);
|
||||
|
||||
if (compConned(a, b))
|
||||
ret = EDijkstra::shortestPath(from, to, costF, distH, &el);
|
||||
|
||||
if (el.size() < 2 && costF.inf() <= ret) {
|
||||
LOG(VDEBUG) << "Pilot run: no connection between candidate groups,"
|
||||
<< " setting max distance to 1";
|
||||
return HopBand{0, 1, 0, 0};
|
||||
}
|
||||
|
||||
// cache the found path, will save a few dijkstra iterations
|
||||
nestedCache(&el, from, costF, rAttrs);
|
||||
|
||||
auto na = el.back()->getFrom();
|
||||
auto nb = el.front()->getFrom();
|
||||
|
||||
double maxStrD = 0;
|
||||
|
||||
for (auto e : to) {
|
||||
double d = static_cast<double>(static_cast<double>(
|
||||
util::geo::webMercMeterDist(*el.front()->getFrom()->pl().getGeom(),
|
||||
*e->getTo()->pl().getGeom())));
|
||||
if (d > maxStrD) maxStrD = d;
|
||||
}
|
||||
|
||||
// TODO(patrick): derive the punish level here automatically
|
||||
double maxD = std::max(ret.getValue(), pend * rOpts.levelPunish[2]) * 3;
|
||||
double minD = ret.getValue();
|
||||
|
||||
LOG(VDEBUG) << "Pilot run: min distance between two groups is "
|
||||
<< ret.getValue() << " (between nodes " << na << " and " << nb
|
||||
<< "), using a max routing distance of " << maxD << ". The max"
|
||||
<< " straight line distance from the pilot target to any other "
|
||||
"target node was"
|
||||
<< " " << maxStrD << ".";
|
||||
|
||||
return HopBand{minD, maxD, el.front(), maxStrD};
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeListHops Router::routeGreedy(const NodeCandRoute& route,
|
||||
const RoutingAttrs& rAttrs,
|
||||
const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const {
|
||||
if (route.size() < 2) return EdgeListHops();
|
||||
EdgeListHops ret(route.size() - 1);
|
||||
|
||||
for (size_t i = 0; i < route.size() - 1; i++) {
|
||||
const trgraph::StatGroup* tgGrp = 0;
|
||||
std::set<trgraph::Node *> from, to;
|
||||
for (auto c : route[i]) from.insert(c.nd);
|
||||
for (auto c : route[i + 1]) to.insert(c.nd);
|
||||
if (route[i + 1].begin()->nd->pl().getSI())
|
||||
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
|
||||
|
||||
NCostFunc cost(rAttrs, rOpts, rest, tgGrp);
|
||||
NDistHeur dist(rOpts, to);
|
||||
|
||||
NodeList nodesRet;
|
||||
EdgeListHop hop;
|
||||
Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet);
|
||||
|
||||
if (nodesRet.size() > 1) {
|
||||
// careful: nodesRet is reversed!
|
||||
hop.start = nodesRet.back();
|
||||
hop.end = nodesRet.front();
|
||||
} else {
|
||||
// just take the first candidate if no route could be found
|
||||
hop.start = *from.begin();
|
||||
hop.end = *to.begin();
|
||||
}
|
||||
|
||||
ret[i] = hop;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeListHops Router::routeGreedy2(const NodeCandRoute& route,
|
||||
const RoutingAttrs& rAttrs,
|
||||
const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const {
|
||||
if (route.size() < 2) return EdgeListHops();
|
||||
EdgeListHops ret(route.size() - 1);
|
||||
|
||||
for (size_t i = 0; i < route.size() - 1; i++) {
|
||||
const trgraph::StatGroup* tgGrp = 0;
|
||||
std::set<trgraph::Node *> from, to;
|
||||
|
||||
if (i == 0)
|
||||
for (auto c : route[i]) from.insert(c.nd);
|
||||
else
|
||||
from.insert(const_cast<trgraph::Node*>(ret[i - 1].end));
|
||||
|
||||
for (auto c : route[i + 1]) to.insert(c.nd);
|
||||
|
||||
if (route[i + 1].begin()->nd->pl().getSI())
|
||||
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
|
||||
|
||||
NCostFunc cost(rAttrs, rOpts, rest, tgGrp);
|
||||
NDistHeur dist(rOpts, to);
|
||||
|
||||
NodeList nodesRet;
|
||||
EdgeListHop hop;
|
||||
Dijkstra::shortestPath(from, to, cost, dist, &hop.edges, &nodesRet);
|
||||
if (nodesRet.size() > 1) {
|
||||
// careful: nodesRet is reversed!
|
||||
hop.start = nodesRet.back();
|
||||
hop.end = nodesRet.front();
|
||||
} else {
|
||||
// just take the first candidate if no route could be found
|
||||
hop.start = *from.begin();
|
||||
hop.end = *to.begin();
|
||||
}
|
||||
|
||||
ret[i] = hop;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeListHops Router::route(const NodeCandRoute& route,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest,
|
||||
router::Graph* cgraph) const {
|
||||
if (route.size() < 2) return EdgeListHops();
|
||||
EdgeListHops ret(route.size() - 1);
|
||||
|
||||
CombCostFunc ccost(rOpts);
|
||||
router::Node* source = cgraph->addNd();
|
||||
router::Node* sink = cgraph->addNd();
|
||||
CombNodeMap nodes;
|
||||
CombNodeMap nextNodes;
|
||||
|
||||
for (size_t i = 0; i < route[0].size(); i++) {
|
||||
for (const auto* e : route[0][i].nd->getAdjListOut()) {
|
||||
// we can be sure that each edge is exactly assigned to only one
|
||||
// node because the transitgraph is directed
|
||||
nodes[e] = cgraph->addNd(route[0][i].nd);
|
||||
cgraph->addEdg(source, nodes[e])
|
||||
->pl()
|
||||
.setCost(EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
route[0][i].pen, 0));
|
||||
}
|
||||
}
|
||||
|
||||
size_t iters = EDijkstra::ITERS;
|
||||
double itPerSecTot = 0;
|
||||
size_t n = 0;
|
||||
for (size_t i = 0; i < route.size() - 1; i++) {
|
||||
nextNodes.clear();
|
||||
HopBand hopBand = getHopBand(route[i], route[i + 1], rAttrs, rOpts, rest);
|
||||
|
||||
const trgraph::StatGroup* tgGrp = 0;
|
||||
if (route[i + 1].begin()->nd->pl().getSI())
|
||||
tgGrp = route[i + 1].begin()->nd->pl().getSI()->getGroup();
|
||||
|
||||
std::set<trgraph::Edge*> froms;
|
||||
for (const auto& fr : route[i]) {
|
||||
froms.insert(fr.nd->getAdjListOut().begin(),
|
||||
fr.nd->getAdjListOut().end());
|
||||
}
|
||||
|
||||
for (auto eFr : froms) {
|
||||
router::Node* cNodeFr = nodes.find(eFr)->second;
|
||||
|
||||
EdgeSet tos;
|
||||
std::map<trgraph::Edge*, router::Edge*> edges;
|
||||
std::map<trgraph::Edge*, double> pens;
|
||||
std::unordered_map<trgraph::Edge*, EdgeList*> edgeLists;
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost> costs;
|
||||
|
||||
assert(route[i + 1].size());
|
||||
|
||||
for (const auto& to : route[i + 1]) {
|
||||
assert(to.nd->getAdjListOut().size());
|
||||
for (auto eTo : to.nd->getAdjListOut()) {
|
||||
tos.insert(eTo);
|
||||
if (!nextNodes.count(eTo)) nextNodes[eTo] = cgraph->addNd(to.nd);
|
||||
if (i == route.size() - 2) cgraph->addEdg(nextNodes[eTo], sink);
|
||||
|
||||
auto* ce = cgraph->addEdg(cNodeFr, nextNodes[eTo]);
|
||||
edges[eTo] = ce;
|
||||
pens[eTo] = to.pen;
|
||||
|
||||
edgeLists[eTo] = ce->pl().getEdges();
|
||||
ce->pl().setStartNode(eFr->getFrom());
|
||||
// for debugging
|
||||
ce->pl().setStartEdge(eFr);
|
||||
|
||||
ce->pl().setEndNode(to.nd);
|
||||
// for debugging
|
||||
ce->pl().setEndEdge(eTo);
|
||||
}
|
||||
}
|
||||
|
||||
size_t iters = EDijkstra::ITERS;
|
||||
auto t1 = TIME();
|
||||
|
||||
assert(tos.size());
|
||||
assert(froms.size());
|
||||
|
||||
hops(eFr, froms, tos, tgGrp, edgeLists, &costs, rAttrs, rOpts, rest,
|
||||
hopBand);
|
||||
double itPerSec =
|
||||
(static_cast<double>(EDijkstra::ITERS - iters)) / TOOK(t1, TIME());
|
||||
n++;
|
||||
itPerSecTot += itPerSec;
|
||||
|
||||
LOG(VDEBUG) << "from " << eFr << ": 1-" << tos.size() << " ("
|
||||
<< route[i + 1].size() << " nodes) hop took "
|
||||
<< EDijkstra::ITERS - iters << " iterations, "
|
||||
<< TOOK(t1, TIME()) << "ms (tput: " << itPerSec << " its/ms)";
|
||||
for (auto& kv : edges) {
|
||||
kv.second->pl().setCost(
|
||||
EdgeCost(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pens[kv.first], 0) +
|
||||
costs[kv.first]);
|
||||
|
||||
if (kv.second->pl().getEdges()->size()) {
|
||||
if (kv.second->pl().getEdges() &&
|
||||
kv.second->pl().getEdges()->size()) {
|
||||
// the reach edge is included, but we dont want it in the geometry
|
||||
kv.second->pl().getEdges()->erase(
|
||||
kv.second->pl().getEdges()->begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(nodes, nextNodes);
|
||||
}
|
||||
|
||||
LOG(VDEBUG) << "Hops took " << EDijkstra::ITERS - iters << " iterations,"
|
||||
<< " average tput was " << (itPerSecTot / n) << " its/ms";
|
||||
|
||||
iters = EDijkstra::ITERS;
|
||||
std::vector<router::Edge*> res;
|
||||
EDijkstra::shortestPath(source, sink, ccost, &res);
|
||||
size_t j = 0;
|
||||
|
||||
LOG(VDEBUG) << "Optim graph solve took " << EDijkstra::ITERS - iters
|
||||
<< " iterations.";
|
||||
|
||||
for (auto i = res.rbegin(); i != res.rend(); i++) {
|
||||
const auto e = *i;
|
||||
if (e->getFrom() != source && e->getTo() != sink) {
|
||||
assert(e->pl().frontNode());
|
||||
assert(e->pl().backNode());
|
||||
|
||||
ret[j] = EdgeListHop{std::move(*e->pl().getEdges()), e->pl().frontNode(),
|
||||
e->pl().backNode()};
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(ret.size() == j);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Router::hops(trgraph::Edge* from, const std::set<trgraph::Edge*>& froms,
|
||||
const std::set<trgraph::Edge*> tos,
|
||||
const trgraph::StatGroup* tgGrp,
|
||||
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest, HopBand hopB) const {
|
||||
std::set<trgraph::Edge*> rem;
|
||||
|
||||
CostFunc cost(rAttrs, rOpts, rest, tgGrp, hopB.maxD);
|
||||
|
||||
const auto& cached = getCachedHops(from, tos, edgesRet, rCosts, rAttrs);
|
||||
|
||||
for (auto e : cached) {
|
||||
// shortcut: if the nodes lie in two different connected components,
|
||||
// the distance between them is trivially infinite
|
||||
if (e == from || e->getFrom() == from->getFrom() ||
|
||||
from->getFrom()->pl().getComp() != e->getTo()->pl().getComp() ||
|
||||
e->pl().oneWay() == 2 || from->pl().oneWay() == 2) {
|
||||
(*rCosts)[e] = cost.inf();
|
||||
} else {
|
||||
rem.insert(e);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(VDEBUG) << "From cache: " << tos.size() - rem.size()
|
||||
<< ", have to cal: " << rem.size();
|
||||
|
||||
if (rem.size()) {
|
||||
DistHeur dist(from->getFrom()->pl().getComp()->minEdgeLvl, rOpts, rem);
|
||||
const auto& ret = EDijkstra::shortestPath(from, rem, cost, dist, edgesRet);
|
||||
for (const auto& kv : ret) {
|
||||
nestedCache(edgesRet.at(kv.first), froms, cost, rAttrs);
|
||||
|
||||
(*rCosts)[kv.first] = kv.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Router::nestedCache(const EdgeList* el,
|
||||
const std::set<trgraph::Edge*>& froms,
|
||||
const CostFunc& cost,
|
||||
const RoutingAttrs& rAttrs) const {
|
||||
if (el->size() == 0) return;
|
||||
// iterate over result edges backwards
|
||||
EdgeList curEdges;
|
||||
EdgeCost curCost;
|
||||
|
||||
size_t j = 0;
|
||||
|
||||
for (auto i = el->begin(); i < el->end(); i++) {
|
||||
if (curEdges.size()) {
|
||||
curCost = curCost + cost(*i, (*i)->getTo(), curEdges.back());
|
||||
}
|
||||
|
||||
curEdges.push_back(*i);
|
||||
|
||||
if (froms.count(*i)) {
|
||||
EdgeCost startC = cost(0, 0, *i) + curCost;
|
||||
cache(*i, el->front(), startC, &curEdges, rAttrs);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::set<pfaedle::trgraph::Edge*> Router::getCachedHops(
|
||||
trgraph::Edge* from, const std::set<trgraph::Edge*>& tos,
|
||||
const std::unordered_map<trgraph::Edge*, EdgeList*>& edgesRet,
|
||||
std::unordered_map<trgraph::Edge*, EdgeCost>* rCosts,
|
||||
const RoutingAttrs& rAttrs) const {
|
||||
std::set<trgraph::Edge*> ret;
|
||||
for (auto to : tos) {
|
||||
if ((*_cache[omp_get_thread_num()])[rAttrs][from].count(to)) {
|
||||
const auto& cv = (*_cache[omp_get_thread_num()])[rAttrs][from][to];
|
||||
(*rCosts)[to] = cv.first;
|
||||
*edgesRet.at(to) = cv.second;
|
||||
} else {
|
||||
ret.insert(to);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Router::cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
|
||||
EdgeList* edges, const RoutingAttrs& rAttrs) const {
|
||||
if (from == to) return;
|
||||
(*_cache[omp_get_thread_num()])[rAttrs][from][to] =
|
||||
std::pair<EdgeCost, EdgeList>(c, *edges);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
size_t Router::getCacheNumber() const { return _cache.size(); }
|
194
src/pfaedle/router/Router.h
Normal file
194
src/pfaedle/router/Router.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_ROUTER_H_
|
||||
#define PFAEDLE_ROUTER_ROUTER_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include "pfaedle/osm/Restrictor.h"
|
||||
#include "pfaedle/router/Graph.h"
|
||||
#include "pfaedle/router/Misc.h"
|
||||
#include "pfaedle/router/RoutingAttrs.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
#include "util/graph/Dijkstra.h"
|
||||
#include "util/graph/EDijkstra.h"
|
||||
|
||||
using util::graph::EDijkstra;
|
||||
using util::graph::Dijkstra;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
typedef std::unordered_map<const trgraph::Edge*, router::Node*> CombNodeMap;
|
||||
typedef std::pair<size_t, size_t> HId;
|
||||
typedef std::map<
|
||||
RoutingAttrs,
|
||||
std::unordered_map<const trgraph::Edge*,
|
||||
std::unordered_map<const trgraph::Edge*,
|
||||
std::pair<EdgeCost, EdgeList> > > >
|
||||
Cache;
|
||||
|
||||
struct HopBand {
|
||||
double minD;
|
||||
double maxD;
|
||||
const trgraph::Edge* nearest;
|
||||
double maxInGrpDist;
|
||||
};
|
||||
|
||||
struct CostFunc
|
||||
: public EDijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
CostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp,
|
||||
double max)
|
||||
: _rAttrs(rAttrs),
|
||||
_rOpts(rOpts),
|
||||
_res(res),
|
||||
_max(max),
|
||||
_tgGrp(tgGrp),
|
||||
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _max, 0) {}
|
||||
|
||||
const RoutingAttrs& _rAttrs;
|
||||
const RoutingOpts& _rOpts;
|
||||
const osm::Restrictor& _res;
|
||||
double _max;
|
||||
const trgraph::StatGroup* _tgGrp;
|
||||
EdgeCost _inf;
|
||||
|
||||
EdgeCost operator()(const trgraph::Edge* from, const trgraph::Node* n,
|
||||
const trgraph::Edge* to) const;
|
||||
EdgeCost inf() const { return _inf; }
|
||||
|
||||
double transitLineCmp(const trgraph::EdgePL& e) const;
|
||||
};
|
||||
|
||||
struct NCostFunc
|
||||
: public Dijkstra::CostFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
NCostFunc(const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& res, const trgraph::StatGroup* tgGrp)
|
||||
: _rAttrs(rAttrs),
|
||||
_rOpts(rOpts),
|
||||
_res(res),
|
||||
_tgGrp(tgGrp),
|
||||
_inf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
std::numeric_limits<double>::infinity(), 0) {}
|
||||
|
||||
const RoutingAttrs& _rAttrs;
|
||||
const RoutingOpts& _rOpts;
|
||||
const osm::Restrictor& _res;
|
||||
const trgraph::StatGroup* _tgGrp;
|
||||
EdgeCost _inf;
|
||||
|
||||
EdgeCost operator()(const trgraph::Node* from, const trgraph::Edge* e,
|
||||
const trgraph::Node* to) const;
|
||||
EdgeCost inf() const { return _inf; }
|
||||
|
||||
double transitLineCmp(const trgraph::EdgePL& e) const;
|
||||
};
|
||||
|
||||
struct DistHeur
|
||||
: public EDijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
DistHeur(uint8_t minLvl, const RoutingOpts& rOpts,
|
||||
const std::set<trgraph::Edge*>& tos);
|
||||
|
||||
const RoutingOpts& _rOpts;
|
||||
uint8_t _lvl;
|
||||
FPoint _center;
|
||||
double _maxCentD;
|
||||
EdgeCost operator()(const trgraph::Edge* a,
|
||||
const std::set<trgraph::Edge*>& b) const;
|
||||
};
|
||||
|
||||
struct NDistHeur
|
||||
: public Dijkstra::HeurFunc<trgraph::NodePL, trgraph::EdgePL, EdgeCost> {
|
||||
NDistHeur(const RoutingOpts& rOpts, const std::set<trgraph::Node*>& tos);
|
||||
|
||||
const RoutingOpts& _rOpts;
|
||||
FPoint _center;
|
||||
double _maxCentD;
|
||||
EdgeCost operator()(const trgraph::Node* a,
|
||||
const std::set<trgraph::Node*>& b) const;
|
||||
};
|
||||
|
||||
struct CombCostFunc
|
||||
: public EDijkstra::CostFunc<router::NodePL, router::EdgePL, double> {
|
||||
explicit CombCostFunc(const RoutingOpts& rOpts) : _rOpts(rOpts) {}
|
||||
|
||||
const RoutingOpts& _rOpts;
|
||||
|
||||
double operator()(const router::Edge* from, const router::Node* n,
|
||||
const router::Edge* to) const;
|
||||
double inf() const { return std::numeric_limits<double>::infinity(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Finds the most likely route of schedule-based vehicle between stops in a
|
||||
* physical transportation network
|
||||
*/
|
||||
class Router {
|
||||
public:
|
||||
// Init this router with caches for numThreads threads
|
||||
Router(const trgraph::Graph& g, size_t numThreads);
|
||||
~Router();
|
||||
|
||||
// Find the most likely path through the graph for a node candidate route.
|
||||
EdgeListHops route(const NodeCandRoute& route, const RoutingAttrs& rAttrs,
|
||||
const RoutingOpts& rOpts, const osm::Restrictor& rest,
|
||||
router::Graph* cgraph) const;
|
||||
|
||||
// Find the most likely path through cgraph for a node candidate route, but
|
||||
// based on a greedy node to node approach
|
||||
EdgeListHops routeGreedy(const NodeCandRoute& route,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const;
|
||||
|
||||
// Find the most likely path through cgraph for a node candidate route, but
|
||||
// based on a greedy node to node set approach
|
||||
EdgeListHops routeGreedy2(const NodeCandRoute& route,
|
||||
const RoutingAttrs& rAttrs,
|
||||
const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const;
|
||||
|
||||
// Return the number of thread caches this router was initialized with
|
||||
size_t getCacheNumber() const;
|
||||
|
||||
private:
|
||||
const trgraph::Graph& _g;
|
||||
|
||||
mutable std::vector<Cache*> _cache;
|
||||
HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
|
||||
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,
|
||||
const osm::Restrictor& rest) const;
|
||||
|
||||
void 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;
|
||||
|
||||
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;
|
||||
|
||||
void cache(trgraph::Edge* from, trgraph::Edge* to, const EdgeCost& c,
|
||||
EdgeList* edges, const RoutingAttrs& rAttrs) const;
|
||||
|
||||
void nestedCache(const EdgeList* el, const std::set<trgraph::Edge*>& froms,
|
||||
const CostFunc& cost, const RoutingAttrs& rAttrs) const;
|
||||
|
||||
bool compConned(const NodeCandGroup& a, const NodeCandGroup& b) const;
|
||||
};
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_ROUTER_H_
|
64
src/pfaedle/router/RoutingAttrs.h
Normal file
64
src/pfaedle/router/RoutingAttrs.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_ROUTINGATTRS_H_
|
||||
#define PFAEDLE_ROUTER_ROUTINGATTRS_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "pfaedle/trgraph/EdgePL.h"
|
||||
|
||||
using pfaedle::trgraph::TransitEdgeLine;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
struct RoutingAttrs {
|
||||
RoutingAttrs() : fromString(""), toString(""), shortName(""), _simiCache() {}
|
||||
std::string fromString;
|
||||
std::string toString;
|
||||
std::string shortName;
|
||||
|
||||
mutable std::map<const TransitEdgeLine*, double> _simiCache;
|
||||
|
||||
double simi(const TransitEdgeLine* line) const {
|
||||
auto i = _simiCache.find(line);
|
||||
if (i != _simiCache.end()) return i->second;
|
||||
|
||||
double cur = 1;
|
||||
if (router::lineSimi(line->shortName, shortName) > 0.5) cur -= 0.33;
|
||||
|
||||
if (line->toStr.empty() || router::statSimi(line->toStr, toString) > 0.5)
|
||||
cur -= 0.33;
|
||||
|
||||
if (line->fromStr.empty() ||
|
||||
router::statSimi(line->fromStr, fromString) > 0.5)
|
||||
cur -= 0.33;
|
||||
|
||||
_simiCache[line] = cur;
|
||||
|
||||
return cur;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const RoutingAttrs& a, const RoutingAttrs& b) {
|
||||
return a.shortName == b.shortName && a.toString == b.toString &&
|
||||
a.fromString == b.fromString;
|
||||
}
|
||||
|
||||
inline bool operator!=(const RoutingAttrs& a, const RoutingAttrs& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline bool operator<(const RoutingAttrs& a, const RoutingAttrs& b) {
|
||||
return a.fromString < b.fromString ||
|
||||
(a.fromString == b.fromString && a.toString < b.toString) ||
|
||||
(a.fromString == b.fromString && a.toString == b.toString &&
|
||||
a.shortName < b.shortName);
|
||||
}
|
||||
|
||||
} // namespace router
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_ROUTER_ROUTINGATTRS_H_
|
627
src/pfaedle/router/ShapeBuilder.cpp
Normal file
627
src/pfaedle/router/ShapeBuilder.cpp
Normal file
|
@ -0,0 +1,627 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <omp.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/eval/Collector.h"
|
||||
#include "pfaedle/osm/OsmBuilder.h"
|
||||
#include "pfaedle/router/ShapeBuilder.h"
|
||||
#include "pfaedle/trgraph/StatGroup.h"
|
||||
#include "util/geo/output/GeoGraphJsonOutput.h"
|
||||
#include "util/geo/output/GeoJsonOutput.h"
|
||||
#include "util/graph/EDijkstra.h"
|
||||
#include "util/log/Log.h"
|
||||
|
||||
using util::geo::FPoint;
|
||||
using util::geo::extendBox;
|
||||
using util::geo::Box;
|
||||
using util::geo::minbox;
|
||||
using util::geo::FLine;
|
||||
using util::geo::webMercMeterDist;
|
||||
using util::geo::webMercToLatLng;
|
||||
using util::geo::latLngToWebMerc;
|
||||
using util::geo::output::GeoGraphJsonOutput;
|
||||
using pfaedle::router::ShapeBuilder;
|
||||
using pfaedle::router::FeedStops;
|
||||
using pfaedle::router::NodeCandGroup;
|
||||
using pfaedle::router::NodeCandRoute;
|
||||
using pfaedle::router::RoutingAttrs;
|
||||
using pfaedle::router::EdgeListHops;
|
||||
using pfaedle::router::Clusters;
|
||||
using pfaedle::osm::BBoxIdx;
|
||||
using ad::cppgtfs::gtfs::Stop;
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
using ad::cppgtfs::gtfs::Feed;
|
||||
using ad::cppgtfs::gtfs::StopTime;
|
||||
using ad::cppgtfs::gtfs::ShapePoint;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
ShapeBuilder::ShapeBuilder(Feed* feed, MOTs mots,
|
||||
const config::MotConfig& motCfg,
|
||||
eval::Collector* ecoll, const config::Config& cfg)
|
||||
: _feed(feed),
|
||||
_mots(mots),
|
||||
_motCfg(motCfg),
|
||||
_ecoll(ecoll),
|
||||
_cfg(cfg),
|
||||
_crouter(_g, omp_get_num_procs()),
|
||||
_curShpCnt(0) {
|
||||
_numThreads = _crouter.getCacheNumber();
|
||||
writeMotStops();
|
||||
|
||||
// TODO(patrick): maybe do this on demand to avoid graph filtering / reading
|
||||
// for input where no routing is necessary (already shape'd)
|
||||
buildGraph();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ShapeBuilder::writeMotStops() {
|
||||
for (auto t : _feed->getTrips()) {
|
||||
if (!_cfg.shapeTripId.empty() && t.second->getId() != _cfg.shapeTripId)
|
||||
continue;
|
||||
if (_mots.count(t.second->getRoute()->getType()) &&
|
||||
_motCfg.mots.count(t.second->getRoute()->getType())) {
|
||||
for (auto st : t.second->getStopTimes()) {
|
||||
_stops[st.getStop()] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FeedStops* ShapeBuilder::getFeedStops() { return &_stops; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const NodeCandGroup& ShapeBuilder::getNodeCands(const Stop* s) const {
|
||||
if (_stops.find(s) == _stops.end() || _stops.at(s) == 0) {
|
||||
return _emptyNCG;
|
||||
}
|
||||
return _stops.at(s)->pl().getSI()->getGroup()->getNodeCands(s);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FLine ShapeBuilder::shapeL(const router::NodeCandRoute& ncr,
|
||||
const router::RoutingAttrs& rAttrs) {
|
||||
const router::EdgeListHops& res = route(ncr, rAttrs);
|
||||
|
||||
FLine l;
|
||||
for (const auto& hop : res) {
|
||||
const trgraph::Node* last = hop.start;
|
||||
if (hop.edges.size() == 0) {
|
||||
l.push_back(*hop.start->pl().getGeom());
|
||||
l.push_back(*hop.end->pl().getGeom());
|
||||
}
|
||||
for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) {
|
||||
const auto* e = *i;
|
||||
if ((e->getFrom() == last) ^ e->pl().isRev()) {
|
||||
l.insert(l.end(), e->pl().getGeom()->begin(), e->pl().getGeom()->end());
|
||||
} else {
|
||||
l.insert(l.end(), e->pl().getGeom()->rbegin(),
|
||||
e->pl().getGeom()->rend());
|
||||
}
|
||||
last = e->getOtherNd(last);
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
FLine ShapeBuilder::shapeL(Trip* trip) {
|
||||
return shapeL(getNCR(trip), getRAttrs(trip));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgeListHops ShapeBuilder::route(const router::NodeCandRoute& ncr,
|
||||
const router::RoutingAttrs& rAttrs) const {
|
||||
router::Graph g;
|
||||
|
||||
if (_cfg.solveMethod == "global") {
|
||||
const router::EdgeListHops& ret =
|
||||
_crouter.route(ncr, rAttrs, _motCfg.routingOpts, _restr, &g);
|
||||
|
||||
// write combination graph
|
||||
if (!_cfg.shapeTripId.empty() && _cfg.writeCombGraph) {
|
||||
LOG(INFO) << "Outputting combgraph.json...";
|
||||
std::ofstream pstr(_cfg.dbgOutputPath + "/combgraph.json");
|
||||
GeoGraphJsonOutput o;
|
||||
o.print(g, pstr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else if (_cfg.solveMethod == "greedy") {
|
||||
return _crouter.routeGreedy(ncr, rAttrs, _motCfg.routingOpts, _restr);
|
||||
} else if (_cfg.solveMethod == "greedy2") {
|
||||
return _crouter.routeGreedy2(ncr, rAttrs, _motCfg.routingOpts, _restr);
|
||||
} else {
|
||||
LOG(ERROR) << "Unknown solution method " << _cfg.solveMethod;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return EdgeListHops();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) const {
|
||||
LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot "
|
||||
<< trip->getRoute()->getType() << "(sn=" << trip->getShortname()
|
||||
<< ", rsn=" << trip->getRoute()->getShortName()
|
||||
<< ", rln=" << trip->getRoute()->getLongName() << ")";
|
||||
Shape ret;
|
||||
ret.hops = route(getNCR(trip), getRAttrs(trip));
|
||||
ret.avgHopDist = avgHopDist(trip);
|
||||
|
||||
LOG(VDEBUG) << "Finished map-matching for #" << trip->getId();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
pfaedle::router::Shape ShapeBuilder::shape(Trip* trip) {
|
||||
LOG(VDEBUG) << "Map-matching shape for trip #" << trip->getId() << " of mot "
|
||||
<< trip->getRoute()->getType() << "(sn=" << trip->getShortname()
|
||||
<< ", rsn=" << trip->getRoute()->getShortName()
|
||||
<< ", rln=" << trip->getRoute()->getLongName() << ")";
|
||||
|
||||
Shape ret;
|
||||
ret.hops = route(getNCR(trip), getRAttrs(trip));
|
||||
ret.avgHopDist = avgHopDist(trip);
|
||||
|
||||
LOG(VDEBUG) << "Finished map-matching for #" << trip->getId();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ShapeBuilder::shape(pfaedle::netgraph::Graph* ng) {
|
||||
TrGraphEdgs gtfsGraph;
|
||||
|
||||
LOG(INFO) << "Clustering trips...";
|
||||
Clusters clusters = clusterTrips(_feed, _mots);
|
||||
LOG(INFO) << "Clustered trips into " << clusters.size() << " clusters.";
|
||||
|
||||
std::map<ad::cppgtfs::gtfs::Shape*, size_t> shpUsage;
|
||||
for (auto t : _feed->getTrips()) {
|
||||
if (t.second->getShape()) shpUsage[t.second->getShape()]++;
|
||||
}
|
||||
|
||||
// to avoid unfair load balance on threads
|
||||
std::random_shuffle(clusters.begin(), clusters.end());
|
||||
|
||||
size_t iters = EDijkstra::ITERS;
|
||||
size_t totiters = EDijkstra::ITERS;
|
||||
size_t oiters = EDijkstra::ITERS;
|
||||
size_t j = 0;
|
||||
|
||||
auto t1 = TIME();
|
||||
auto t2 = TIME();
|
||||
double totAvgDist = 0;
|
||||
size_t totNumTrips = 0;
|
||||
|
||||
#pragma omp parallel for num_threads(_numThreads)
|
||||
for (size_t i = 0; i < clusters.size(); i++) {
|
||||
j++;
|
||||
|
||||
if (j % 10 == 0) {
|
||||
#pragma omp critical
|
||||
{
|
||||
LOG(INFO) << "@ " << j << " / " << clusters.size() << " ("
|
||||
<< (static_cast<int>((j * 1.0) / clusters.size() * 100))
|
||||
<< "%, " << (EDijkstra::ITERS - oiters) << " iters, tput "
|
||||
<< (static_cast<double>(EDijkstra::ITERS - oiters)) /
|
||||
TOOK(t1, TIME())
|
||||
<< " iters/ms"
|
||||
<< ", " << (10.0 / (TOOK(t1, TIME()) / 1000))
|
||||
<< " trips/sec)";
|
||||
|
||||
oiters = EDijkstra::ITERS;
|
||||
t1 = TIME();
|
||||
}
|
||||
}
|
||||
|
||||
// explicitly call const version of shape here for thread safety
|
||||
const Shape& cshp =
|
||||
const_cast<const ShapeBuilder&>(*this).shape(clusters[i][0]);
|
||||
totAvgDist += cshp.avgHopDist;
|
||||
|
||||
if (_cfg.buildTransitGraph) {
|
||||
#pragma omp critical
|
||||
{ writeTransitGraph(cshp, >fsGraph, clusters[i]); }
|
||||
}
|
||||
|
||||
std::vector<double> distances;
|
||||
ad::cppgtfs::gtfs::Shape* shp =
|
||||
getGtfsShape(cshp, clusters[i][0], &distances);
|
||||
|
||||
LOG(DEBUG) << "Took " << EDijkstra::ITERS - iters << " iterations.";
|
||||
iters = EDijkstra::ITERS;
|
||||
|
||||
totNumTrips += clusters[i].size();
|
||||
|
||||
for (auto t : clusters[i]) {
|
||||
if (_cfg.evaluate) {
|
||||
_ecoll->add(t, t->getShape(), shp, distances);
|
||||
}
|
||||
|
||||
if (t->getShape() && shpUsage[t->getShape()] > 0) {
|
||||
shpUsage[t->getShape()]--;
|
||||
if (shpUsage[t->getShape()] == 0) {
|
||||
_feed->getShapes().remove(t->getShape()->getId());
|
||||
delete t->getShape();
|
||||
}
|
||||
}
|
||||
setShape(t, shp, distances);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Done.";
|
||||
LOG(INFO) << "Matched " << totNumTrips << " trips in " << clusters.size()
|
||||
<< " clusters.";
|
||||
LOG(INFO) << "Took " << (EDijkstra::ITERS - totiters)
|
||||
<< " iterations in total.";
|
||||
LOG(INFO) << "Took " << TOOK(t2, TIME()) << " ms in total.";
|
||||
LOG(INFO) << "Total avg. tput "
|
||||
<< (static_cast<double>(EDijkstra::ITERS - totiters)) /
|
||||
TOOK(t2, TIME())
|
||||
<< " iters/sec";
|
||||
LOG(INFO) << "Total avg. trip tput "
|
||||
<< (clusters.size() / (TOOK(t2, TIME()) / 1000)) << " trips/sec";
|
||||
LOG(INFO) << "Avg hop distance was "
|
||||
<< (totAvgDist / static_cast<double>(clusters.size())) << " meters";
|
||||
|
||||
if (_cfg.buildTransitGraph) {
|
||||
LOG(INFO) << "Building transit network graph...";
|
||||
buildTrGraph(>fsGraph, ng);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ShapeBuilder::setShape(Trip* t, ad::cppgtfs::gtfs::Shape* s,
|
||||
const std::vector<double>& distances) {
|
||||
assert(distances.size() == t->getStopTimes().size());
|
||||
// set distances
|
||||
size_t i = 0;
|
||||
for (const StopTime& st : t->getStopTimes()) {
|
||||
const_cast<StopTime&>(st).setShapeDistanceTravelled(distances[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
t->setShape(s);
|
||||
|
||||
std::lock_guard<std::mutex> guard(_shpMutex);
|
||||
_feed->getShapes().add(s);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
ad::cppgtfs::gtfs::Shape* ShapeBuilder::getGtfsShape(
|
||||
const Shape& shp, Trip* t, std::vector<double>* hopDists) {
|
||||
ad::cppgtfs::gtfs::Shape* ret =
|
||||
new ad::cppgtfs::gtfs::Shape(getFreeShapeId(t));
|
||||
|
||||
assert(shp.hops.size() == t->getStopTimes().size() - 1);
|
||||
|
||||
size_t seq = 0;
|
||||
double dist = -1;
|
||||
double lastDist = -1;
|
||||
hopDists->push_back(0);
|
||||
FPoint last(0, 0);
|
||||
for (const auto& hop : shp.hops) {
|
||||
const trgraph::Node* l = hop.start;
|
||||
if (hop.edges.size() == 0) {
|
||||
FPoint ll = webMercToLatLng<float>(hop.start->pl().getGeom()->get<0>(),
|
||||
hop.start->pl().getGeom()->get<1>());
|
||||
|
||||
if (dist > -0.5)
|
||||
dist += webMercMeterDist(last, *hop.start->pl().getGeom());
|
||||
else
|
||||
dist = 0;
|
||||
|
||||
last = *hop.start->pl().getGeom();
|
||||
|
||||
if (dist - lastDist > 0.01) {
|
||||
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
|
||||
seq++;
|
||||
lastDist = dist;
|
||||
}
|
||||
|
||||
dist += webMercMeterDist(last, *hop.end->pl().getGeom());
|
||||
last = *hop.end->pl().getGeom();
|
||||
|
||||
if (dist - lastDist > 0.01) {
|
||||
ll = webMercToLatLng<float>(hop.end->pl().getGeom()->get<0>(),
|
||||
hop.end->pl().getGeom()->get<1>());
|
||||
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
|
||||
seq++;
|
||||
lastDist = dist;
|
||||
}
|
||||
}
|
||||
for (auto i = hop.edges.rbegin(); i != hop.edges.rend(); i++) {
|
||||
const auto* e = *i;
|
||||
if ((e->getFrom() == l) ^ e->pl().isRev()) {
|
||||
for (size_t i = 0; i < e->pl().getGeom()->size(); i++) {
|
||||
const FPoint& cur = (*e->pl().getGeom())[i];
|
||||
if (dist > -0.5)
|
||||
dist += webMercMeterDist(last, cur);
|
||||
else
|
||||
dist = 0;
|
||||
last = cur;
|
||||
if (dist - lastDist > 0.01) {
|
||||
FPoint ll = webMercToLatLng<float>(cur.get<0>(), cur.get<1>());
|
||||
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
|
||||
seq++;
|
||||
lastDist = dist;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int64_t i = e->pl().getGeom()->size() - 1; i >= 0; i--) {
|
||||
const FPoint& cur = (*e->pl().getGeom())[i];
|
||||
if (dist > -0.5)
|
||||
dist += webMercMeterDist(last, cur);
|
||||
else
|
||||
dist = 0;
|
||||
last = cur;
|
||||
if (dist - lastDist > 0.01) {
|
||||
FPoint ll = webMercToLatLng<float>(cur.get<0>(), cur.get<1>());
|
||||
ret->addPoint(ShapePoint(ll.get<1>(), ll.get<0>(), dist, seq));
|
||||
seq++;
|
||||
lastDist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
l = e->getOtherNd(l);
|
||||
}
|
||||
|
||||
hopDists->push_back(lastDist);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string ShapeBuilder::getFreeShapeId(Trip* trip) {
|
||||
std::string ret;
|
||||
std::lock_guard<std::mutex> guard(_shpMutex);
|
||||
while (!ret.size() || _feed->getShapes().get(ret)) {
|
||||
_curShpCnt++;
|
||||
ret = "shp_";
|
||||
ret += std::to_string(trip->getRoute()->getType());
|
||||
ret += "_" + std::to_string(_curShpCnt);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) {
|
||||
auto i = _rAttrs.find(trip);
|
||||
|
||||
if (i == _rAttrs.end()) {
|
||||
router::RoutingAttrs ret;
|
||||
|
||||
const auto& lnormzer = _motCfg.osmBuildOpts.lineNormzer;
|
||||
|
||||
ret.shortName = lnormzer(trip->getRoute()->getShortName());
|
||||
|
||||
if (ret.shortName.empty()) ret.shortName = lnormzer(trip->getShortname());
|
||||
|
||||
if (ret.shortName.empty())
|
||||
ret.shortName = lnormzer(trip->getRoute()->getLongName());
|
||||
|
||||
ret.fromString = _motCfg.osmBuildOpts.statNormzer(
|
||||
trip->getStopTimes().begin()->getStop()->getName());
|
||||
ret.toString = _motCfg.osmBuildOpts.statNormzer(
|
||||
(--trip->getStopTimes().end())->getStop()->getName());
|
||||
|
||||
return _rAttrs
|
||||
.insert(std::pair<const Trip*, router::RoutingAttrs>(trip, ret))
|
||||
.first->second;
|
||||
} else {
|
||||
return i->second;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const RoutingAttrs& ShapeBuilder::getRAttrs(const Trip* trip) const {
|
||||
return _rAttrs.find(trip)->second;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
BBoxIdx ShapeBuilder::getPaddedGtfsBox(const Feed* feed, double pad,
|
||||
const MOTs& mots,
|
||||
const std::string& tid) {
|
||||
osm::BBoxIdx box(pad);
|
||||
for (const auto& t : feed->getTrips()) {
|
||||
if (!tid.empty() && t.second->getId() != tid) continue;
|
||||
if (mots.count(t.second->getRoute()->getType())) {
|
||||
Box<float> cur = minbox<float>();
|
||||
for (const auto& st : t.second->getStopTimes()) {
|
||||
cur = extendBox(
|
||||
Point<float>(st.getStop()->getLng(), st.getStop()->getLat()), cur);
|
||||
}
|
||||
box.add(cur);
|
||||
}
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ShapeBuilder::buildGraph() {
|
||||
LOG(INFO) << "Reading " << _cfg.osmPath << " ... ";
|
||||
osm::OsmBuilder osmBuilder;
|
||||
|
||||
osm::BBoxIdx box = getPaddedGtfsBox(_feed, 2500, _mots, _cfg.shapeTripId);
|
||||
|
||||
osmBuilder.read(_cfg.osmPath, _motCfg.osmBuildOpts, &_g, box, _cfg.gridSize,
|
||||
getFeedStops(), &_restr);
|
||||
|
||||
for (auto& feedStop : *getFeedStops()) {
|
||||
if (feedStop.second) {
|
||||
feedStop.second->pl().getSI()->getGroup()->writePens(
|
||||
_motCfg.osmBuildOpts.trackNormzer,
|
||||
_motCfg.routingOpts.platformUnmatchedPen,
|
||||
_motCfg.routingOpts.stationDistPenFactor,
|
||||
_motCfg.routingOpts.nonOsmPen);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "Done.";
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NodeCandRoute ShapeBuilder::getNCR(Trip* trip) const {
|
||||
router::NodeCandRoute ncr(trip->getStopTimes().size());
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for (const auto& st : trip->getStopTimes()) {
|
||||
ncr[i] = getNodeCands(st.getStop());
|
||||
i++;
|
||||
}
|
||||
return ncr;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double ShapeBuilder::avgHopDist(Trip* trip) const {
|
||||
size_t i = 0;
|
||||
double sum = 0;
|
||||
|
||||
const Stop* prev = 0;
|
||||
|
||||
for (const auto& st : trip->getStopTimes()) {
|
||||
if (!prev) {
|
||||
prev = st.getStop();
|
||||
continue;
|
||||
}
|
||||
auto a = util::geo::latLngToWebMerc<float>(prev->getLat(), prev->getLng());
|
||||
auto b = util::geo::latLngToWebMerc<float>(st.getStop()->getLat(),
|
||||
st.getStop()->getLng());
|
||||
sum += util::geo::webMercMeterDist(a, b);
|
||||
|
||||
prev = st.getStop();
|
||||
i++;
|
||||
}
|
||||
return sum / static_cast<double>(i);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
Clusters ShapeBuilder::clusterTrips(Feed* f, MOTs mots) {
|
||||
// building an index [start station, end station] -> [cluster]
|
||||
|
||||
std::map<StopPair, std::vector<size_t>> clusterIdx;
|
||||
|
||||
size_t j = 0;
|
||||
|
||||
Clusters ret;
|
||||
for (const auto& trip : f->getTrips()) {
|
||||
// if (trip.second->getId() != "L5Cvl_T01") continue;
|
||||
if (trip.second->getShape() && !_cfg.dropShapes) continue;
|
||||
if (trip.second->getStopTimes().size() < 2) continue;
|
||||
if (!mots.count(trip.second->getRoute()->getType()) ||
|
||||
!_motCfg.mots.count(trip.second->getRoute()->getType()))
|
||||
continue;
|
||||
bool found = false;
|
||||
auto spair = StopPair(trip.second->getStopTimes().begin()->getStop(),
|
||||
trip.second->getStopTimes().rbegin()->getStop());
|
||||
const auto& c = clusterIdx[spair];
|
||||
|
||||
for (size_t i = 0; i < c.size(); i++) {
|
||||
j++;
|
||||
if (routingEqual(ret[c[i]][0], trip.second)) {
|
||||
ret[c[i]].push_back(trip.second);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ret.push_back({trip.second});
|
||||
// explicit call to write render attrs to cache
|
||||
getRAttrs(trip.second);
|
||||
clusterIdx[spair].push_back(ret.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool ShapeBuilder::routingEqual(const Stop* a, const Stop* b) {
|
||||
if (a == b) return true; // trivial
|
||||
|
||||
auto namea = _motCfg.osmBuildOpts.statNormzer(a->getName());
|
||||
auto nameb = _motCfg.osmBuildOpts.statNormzer(b->getName());
|
||||
if (namea != nameb) return false;
|
||||
|
||||
auto tracka = _motCfg.osmBuildOpts.trackNormzer(a->getPlatformCode());
|
||||
auto trackb = _motCfg.osmBuildOpts.trackNormzer(b->getPlatformCode());
|
||||
if (tracka != trackb) return false;
|
||||
|
||||
FPoint ap = util::geo::latLngToWebMerc<float>(a->getLat(), a->getLng());
|
||||
FPoint bp = util::geo::latLngToWebMerc<float>(b->getLat(), b->getLng());
|
||||
|
||||
double d = util::geo::webMercMeterDist(ap, bp);
|
||||
|
||||
if (d > 1) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool ShapeBuilder::routingEqual(Trip* a, Trip* b) {
|
||||
if (a->getStopTimes().size() != b->getStopTimes().size()) return false;
|
||||
if (getRAttrs(a) != getRAttrs(b)) return false;
|
||||
|
||||
auto stb = b->getStopTimes().begin();
|
||||
for (const auto& sta : a->getStopTimes()) {
|
||||
if (!routingEqual(sta.getStop(), stb->getStop())) {
|
||||
return false;
|
||||
}
|
||||
stb++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const pfaedle::trgraph::Graph* ShapeBuilder::getGraph() const { return &_g; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ShapeBuilder::writeTransitGraph(const Shape& shp, TrGraphEdgs* edgs,
|
||||
const Cluster& cluster) const {
|
||||
for (auto hop : shp.hops) {
|
||||
for (const auto* e : hop.edges) {
|
||||
if (e->pl().isRev()) e = _g.getEdg(e->getTo(), e->getFrom());
|
||||
(*edgs)[e].insert(cluster.begin(), cluster.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void ShapeBuilder::buildTrGraph(TrGraphEdgs* edgs,
|
||||
pfaedle::netgraph::Graph* ng) const {
|
||||
std::unordered_map<trgraph::Node*, pfaedle::netgraph::Node*> nodes;
|
||||
|
||||
for (auto ep : *edgs) {
|
||||
auto e = ep.first;
|
||||
pfaedle::netgraph::Node* from = 0;
|
||||
pfaedle::netgraph::Node* to = 0;
|
||||
if (nodes.count(e->getFrom())) from = nodes[e->getFrom()];
|
||||
if (nodes.count(e->getTo())) to = nodes[e->getTo()];
|
||||
if (!from) {
|
||||
from = ng->addNd(*e->getFrom()->pl().getGeom());
|
||||
nodes[e->getFrom()] = from;
|
||||
}
|
||||
if (!to) {
|
||||
to = ng->addNd(*e->getTo()->pl().getGeom());
|
||||
nodes[e->getTo()] = to;
|
||||
}
|
||||
|
||||
ng->addEdg(from, to,
|
||||
pfaedle::netgraph::EdgePL(*e->pl().getGeom(), ep.second));
|
||||
}
|
||||
}
|
120
src/pfaedle/router/ShapeBuilder.h
Normal file
120
src/pfaedle/router/ShapeBuilder.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_ROUTER_SHAPEBUILDER_H_
|
||||
#define PFAEDLE_ROUTER_SHAPEBUILDER_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/config/MotConfig.h"
|
||||
#include "pfaedle/config/PfaedleConfig.h"
|
||||
#include "pfaedle/eval/Collector.h"
|
||||
#include "pfaedle/netgraph/Graph.h"
|
||||
#include "pfaedle/osm/Restrictor.h"
|
||||
#include "pfaedle/router/Misc.h"
|
||||
#include "pfaedle/router/Router.h"
|
||||
#include "pfaedle/trgraph/Graph.h"
|
||||
|
||||
namespace pfaedle {
|
||||
namespace router {
|
||||
|
||||
using ad::cppgtfs::gtfs::Stop;
|
||||
using ad::cppgtfs::gtfs::Trip;
|
||||
using ad::cppgtfs::gtfs::Feed;
|
||||
|
||||
struct Shape {
|
||||
router::EdgeListHops hops;
|
||||
double avgHopDist;
|
||||
};
|
||||
|
||||
typedef std::vector<Trip*> Cluster;
|
||||
typedef std::vector<Cluster> Clusters;
|
||||
typedef std::pair<const Stop*, const Stop*> StopPair;
|
||||
typedef std::unordered_map<const Trip*, router::RoutingAttrs> TripRAttrs;
|
||||
typedef std::unordered_map<const trgraph::Edge*, std::set<const Trip*>>
|
||||
TrGraphEdgs;
|
||||
|
||||
/*
|
||||
* Layer class for the router. Provides an interface for direct usage with
|
||||
* GTFS data
|
||||
*/
|
||||
class ShapeBuilder {
|
||||
public:
|
||||
ShapeBuilder(Feed* feed, MOTs mots, const config::MotConfig& motCfg,
|
||||
eval::Collector* ecoll, const config::Config& cfg);
|
||||
|
||||
void shape(pfaedle::netgraph::Graph* ng);
|
||||
|
||||
router::FeedStops* getFeedStops();
|
||||
|
||||
const NodeCandGroup& getNodeCands(const Stop* s) const;
|
||||
|
||||
util::geo::FLine shapeL(const router::NodeCandRoute& ncr,
|
||||
const router::RoutingAttrs& rAttrs);
|
||||
util::geo::FLine shapeL(Trip* trip);
|
||||
|
||||
pfaedle::router::Shape shape(Trip* trip) const;
|
||||
pfaedle::router::Shape shape(Trip* trip);
|
||||
|
||||
const trgraph::Graph* getGraph() const;
|
||||
|
||||
static osm::BBoxIdx getPaddedGtfsBox(const Feed* feed, double pad,
|
||||
const MOTs& mots,
|
||||
const std::string& tid);
|
||||
|
||||
private:
|
||||
Feed* _feed;
|
||||
MOTs _mots;
|
||||
config::MotConfig _motCfg;
|
||||
eval::Collector* _ecoll;
|
||||
config::Config _cfg;
|
||||
trgraph::Graph _g;
|
||||
router::Router _crouter;
|
||||
|
||||
router::FeedStops _stops;
|
||||
|
||||
NodeCandGroup _emptyNCG;
|
||||
|
||||
size_t _curShpCnt, _numThreads;
|
||||
|
||||
std::mutex _shpMutex;
|
||||
|
||||
TripRAttrs _rAttrs;
|
||||
|
||||
osm::Restrictor _restr;
|
||||
|
||||
void writeMotStops();
|
||||
void buildGraph();
|
||||
|
||||
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(Trip* t);
|
||||
|
||||
ad::cppgtfs::gtfs::Shape* getGtfsShape(const Shape& shp, Trip* t,
|
||||
std::vector<double>* hopDists);
|
||||
|
||||
void setShape(Trip* t, ad::cppgtfs::gtfs::Shape* s,
|
||||
const std::vector<double>& dists);
|
||||
|
||||
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
|
||||
|
||||
#endif // PFAEDLE_ROUTER_SHAPEBUILDER_H_
|
174
src/pfaedle/trgraph/EdgePL.cpp
Normal file
174
src/pfaedle/trgraph/EdgePL.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
// 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 "pfaedle/trgraph/EdgePL.h"
|
||||
|
||||
using pfaedle::trgraph::EdgePL;
|
||||
using pfaedle::trgraph::TransitEdgeLine;
|
||||
|
||||
std::map<util::geo::FLine*, size_t> EdgePL::_flines;
|
||||
std::map<const TransitEdgeLine*, size_t> EdgePL::_tlines;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgePL::EdgePL()
|
||||
: _length(0), _oneWay(0), _hasRestr(false), _rev(false), _lvl(0) {
|
||||
_l = new util::geo::FLine();
|
||||
_flines[_l] = 1;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgePL::EdgePL(const EdgePL& pl) : EdgePL(pl, false) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgePL::EdgePL(const EdgePL& pl, bool geoflat)
|
||||
: _length(pl._length),
|
||||
_oneWay(pl._oneWay),
|
||||
_hasRestr(pl._hasRestr),
|
||||
_rev(pl._rev),
|
||||
_lvl(pl._lvl) {
|
||||
if (geoflat) {
|
||||
_l = pl._l;
|
||||
} else {
|
||||
_l = new util::geo::FLine(*pl._l);
|
||||
}
|
||||
_flines[_l]++;
|
||||
|
||||
for (auto l : _lines) addLine(l);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgePL::~EdgePL() {
|
||||
if (_l) {
|
||||
_flines[_l]--;
|
||||
if (_flines[_l] == 0) delete _l;
|
||||
}
|
||||
|
||||
for (auto l : _lines) unRefTLine(l);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::unRefTLine(const TransitEdgeLine* l) {
|
||||
if (l) {
|
||||
_tlines[l]--;
|
||||
if (_tlines[l] == 0) {
|
||||
delete l;
|
||||
_tlines.erase(_tlines.find(l));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
EdgePL EdgePL::revCopy() const {
|
||||
EdgePL ret(*this);
|
||||
ret.setRev();
|
||||
if (ret.oneWay() == 1)
|
||||
ret.setOneWay(2);
|
||||
else if (ret.oneWay() == 2)
|
||||
ret.setOneWay(1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setLength(double d) { _length = d; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double EdgePL::getLength() const { return _length; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::addLine(const TransitEdgeLine* l) {
|
||||
if (_lines.insert(l).second) {
|
||||
if (_tlines.count(l))
|
||||
_tlines[l]++;
|
||||
else
|
||||
_tlines[l] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::addLines(const std::vector<TransitEdgeLine*>& l) {
|
||||
for (auto line : l) addLine(line);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<const TransitEdgeLine*>& EdgePL::getLines() const {
|
||||
return _lines;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::addPoint(const util::geo::FPoint& p) { _l->push_back(p); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FLine* EdgePL::getGeom() const { return _l; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
util::geo::FLine* EdgePL::getGeom() { return _l; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["m_length"] = std::to_string(_length);
|
||||
(*obj)["oneway"] = std::to_string(static_cast<int>(_oneWay));
|
||||
(*obj)["level"] = std::to_string(_lvl);
|
||||
(*obj)["restriction"] = isRestricted() ? "yes" : "no";
|
||||
|
||||
std::stringstream ss;
|
||||
bool first = false;
|
||||
|
||||
for (auto* l : _lines) {
|
||||
if (first) ss << ",";
|
||||
ss << l->shortName;
|
||||
if (l->fromStr.size() || l->toStr.size()) {
|
||||
ss << "(" << l->fromStr;
|
||||
ss << "->" << l->toStr << ")";
|
||||
}
|
||||
first = true;
|
||||
}
|
||||
|
||||
(*obj)["lines"] = ss.str();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setRestricted() { _hasRestr = true; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool EdgePL::isRestricted() const { return _hasRestr; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint8_t EdgePL::oneWay() const { return _oneWay; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setOneWay(uint8_t dir) { _oneWay = dir; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setOneWay() { _oneWay = 1; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setLvl(uint8_t lvl) { _lvl = lvl; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
uint8_t EdgePL::lvl() const { return _lvl; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void EdgePL::setRev() { _rev = true; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool EdgePL::isRev() const { return _rev; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FPoint& EdgePL::backHop() const {
|
||||
if (isRev()) {
|
||||
return *(++(getGeom()->cbegin()));
|
||||
}
|
||||
return *(++(getGeom()->crbegin()));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FPoint& EdgePL::frontHop() const {
|
||||
if (isRev()) {
|
||||
return *(++(getGeom()->crbegin()));
|
||||
}
|
||||
return *(++(getGeom()->cbegin()));
|
||||
}
|
133
src/pfaedle/trgraph/EdgePL.h
Normal file
133
src/pfaedle/trgraph/EdgePL.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_TRGRAPH_EDGEPL_H_
|
||||
#define PFAEDLE_TRGRAPH_EDGEPL_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "pfaedle/router/Comp.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoEdgePL;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
|
||||
/*
|
||||
* A line occuring on an edge
|
||||
*/
|
||||
struct TransitEdgeLine {
|
||||
std::string fromStr;
|
||||
std::string toStr;
|
||||
std::string shortName;
|
||||
};
|
||||
|
||||
inline bool operator==(const TransitEdgeLine& a, const TransitEdgeLine& b) {
|
||||
return a.fromStr == b.fromStr && a.toStr == b.toStr &&
|
||||
a.shortName == b.shortName;
|
||||
}
|
||||
|
||||
inline bool operator<(const TransitEdgeLine& a, const TransitEdgeLine& b) {
|
||||
return a.fromStr < b.fromStr ||
|
||||
(a.fromStr == b.fromStr && a.toStr < b.toStr) ||
|
||||
(a.fromStr == b.fromStr && a.toStr == b.toStr &&
|
||||
a.shortName < b.shortName);
|
||||
}
|
||||
|
||||
/*
|
||||
* An edge payload class for the transit graph.
|
||||
*/
|
||||
class EdgePL : public GeoEdgePL<float> {
|
||||
public:
|
||||
EdgePL();
|
||||
~EdgePL();
|
||||
EdgePL(const EdgePL& pl);
|
||||
EdgePL(const EdgePL& pl, bool geoFlat);
|
||||
|
||||
// Return the geometry of this edge.
|
||||
const util::geo::FLine* getGeom() const;
|
||||
util::geo::FLine* getGeom();
|
||||
|
||||
// Extends this edge payload's geometry by Point p
|
||||
void addPoint(const util::geo::FPoint& p);
|
||||
|
||||
// Fill obj with k/v pairs describing the parameters of this payload.
|
||||
void getAttrs(std::map<std::string, std::string>* obj) const;
|
||||
|
||||
// Return the length in meters stored for this edge payload
|
||||
double getLength() const;
|
||||
|
||||
// Set the length in meters for this edge payload
|
||||
void setLength(double d);
|
||||
|
||||
// Set this edge as a one way node, either in the default direction of
|
||||
// the edge (no arg), or the direction specified in dir
|
||||
void setOneWay();
|
||||
void setOneWay(uint8_t dir);
|
||||
|
||||
// Mark this payload' edge as having some restrictions
|
||||
void setRestricted();
|
||||
|
||||
// Mark this payload' edge as being secondary to an inversed partner
|
||||
void setRev();
|
||||
|
||||
// True if this edge is secondary to an inversed partner
|
||||
bool isRev() const;
|
||||
|
||||
// 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;
|
||||
|
||||
// Add a TransitedgeLine to this payload's edge
|
||||
void addLine(const TransitEdgeLine* l);
|
||||
|
||||
// Add multiple TransitedgeLine objects to this payload's edge
|
||||
void addLines(const std::vector<TransitEdgeLine*>& l);
|
||||
|
||||
// Return the TransitEdgeLines stored for this payload
|
||||
const std::set<const TransitEdgeLine*>& getLines() const;
|
||||
|
||||
// Returns the last hop of the payload - this is the (n-2)th point in
|
||||
// the payload geometry of length n > 1
|
||||
const util::geo::FPoint& backHop() const;
|
||||
|
||||
// Returns the first hop of the payload - this is the 2nd point in
|
||||
// the payload geometry of length n > 1
|
||||
const util::geo::FPoint& frontHop() const;
|
||||
|
||||
// Obtain an exact copy of this edge, but in reverse.
|
||||
EdgePL revCopy() const;
|
||||
|
||||
private:
|
||||
float _length;
|
||||
uint8_t _oneWay : 2;
|
||||
bool _hasRestr : 1;
|
||||
bool _rev : 1;
|
||||
uint8_t _lvl : 3;
|
||||
|
||||
util::geo::FLine* _l;
|
||||
|
||||
std::set<const TransitEdgeLine*> _lines;
|
||||
|
||||
static void unRefTLine(const TransitEdgeLine* l);
|
||||
|
||||
static std::map<util::geo::FLine*, size_t> _flines;
|
||||
static std::map<const TransitEdgeLine*, size_t> _tlines;
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_TRGRAPH_EDGEPL_H_
|
35
src/pfaedle/trgraph/Graph.h
Normal file
35
src/pfaedle/trgraph/Graph.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_TRGRAPH_GRAPH_H_
|
||||
#define PFAEDLE_TRGRAPH_GRAPH_H_
|
||||
|
||||
#include "pfaedle/trgraph/NodePL.h"
|
||||
#include "pfaedle/trgraph/EdgePL.h"
|
||||
#include "util/graph/UndirGraph.h"
|
||||
#include "util/graph/DirGraph.h"
|
||||
#include "util/geo/Grid.h"
|
||||
|
||||
using util::geo::Grid;
|
||||
using util::geo::Point;
|
||||
using util::geo::Line;
|
||||
using util::geo::FPoint;
|
||||
using util::geo::FLine;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
/*
|
||||
* A graph for physical transit networks
|
||||
*/
|
||||
typedef util::graph::Edge<NodePL, EdgePL> Edge;
|
||||
typedef util::graph::Node<NodePL, EdgePL> Node;
|
||||
typedef util::graph::DirGraph<NodePL, EdgePL> Graph;
|
||||
typedef Grid<Node*, Point, float> NodeGrid;
|
||||
typedef Grid<Edge*, Line, float> EdgeGrid;
|
||||
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_TRGRAPH_GRAPH_H_
|
124
src/pfaedle/trgraph/NodePL.cpp
Normal file
124
src/pfaedle/trgraph/NodePL.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "pfaedle/trgraph/NodePL.h"
|
||||
#include "pfaedle/trgraph/StatGroup.h"
|
||||
#include "pfaedle/trgraph/StatInfo.h"
|
||||
#include "util/String.h"
|
||||
|
||||
using pfaedle::trgraph::StatInfo;
|
||||
using pfaedle::trgraph::NodePL;
|
||||
using pfaedle::trgraph::Component;
|
||||
|
||||
// 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() : _geom(0, 0), _si(0), _component(0), _vis(0) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NodePL::NodePL(const NodePL& pl)
|
||||
: _geom(pl._geom), _si(0), _component(pl._component), _vis(pl._vis) {
|
||||
if (pl._si) setSI(*(pl._si));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NodePL::NodePL(const util::geo::FPoint& geom)
|
||||
: _geom(geom), _si(0), _component(0), _vis(0) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
NodePL::NodePL(const util::geo::FPoint& geom, const StatInfo& si)
|
||||
: _geom(geom), _si(0), _component(0), _vis(0) {
|
||||
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 { _vis = true; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setNoStat() { _si = 0; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const Component* NodePL::getComp() const { return _component; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setComp(const Component* c) {
|
||||
if (_component == c) return;
|
||||
_component = c;
|
||||
|
||||
// NOT thread safe!
|
||||
if (!_comps.count(c))
|
||||
_comps[c] = 1;
|
||||
else
|
||||
_comps[c]++;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const util::geo::FPoint* NodePL::getGeom() const { return &_geom; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setGeom(const util::geo::FPoint& geom) { _geom = geom; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::getAttrs(std::map<std::string, std::string>* obj) const {
|
||||
(*obj)["component"] = std::to_string(reinterpret_cast<size_t>(_component));
|
||||
(*obj)["dijkstra_vis"] = _vis ? "yes" : "no";
|
||||
if (getSI()) {
|
||||
(*obj)["station_info_ptr"] = util::toString(_si);
|
||||
(*obj)["station_name"] = _si->getName();
|
||||
(*obj)["station_alt_names"] = util::implode(_si->getAltNames(), ",");
|
||||
(*obj)["from_osm"] = _si->isFromOsm() ? "yes" : "no";
|
||||
(*obj)["station_platform"] = _si->getTrack();
|
||||
(*obj)["station_group"] =
|
||||
std::to_string(reinterpret_cast<size_t>(_si->getGroup()));
|
||||
|
||||
std::stringstream gtfsIds;
|
||||
if (_si->getGroup()) {
|
||||
for (auto* s : _si->getGroup()->getStops()) {
|
||||
gtfsIds << s->getId() << " (" << s->getName() << "),";
|
||||
}
|
||||
}
|
||||
|
||||
(*obj)["station_group_stops"] = gtfsIds.str();
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setSI(const StatInfo& si) { _si = new StatInfo(si); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const StatInfo* NodePL::getSI() const {
|
||||
if (isBlocker()) return 0;
|
||||
return _si;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
StatInfo* NodePL::getSI() {
|
||||
if (isBlocker()) return 0;
|
||||
return _si;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void NodePL::setBlocker() { _si = &_blockerSI; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool NodePL::isBlocker() const { return _si == &_blockerSI; }
|
83
src/pfaedle/trgraph/NodePL.h
Normal file
83
src/pfaedle/trgraph/NodePL.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_TRGRAPH_NODEPL_H_
|
||||
#define PFAEDLE_TRGRAPH_NODEPL_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "ad/cppgtfs/gtfs/Feed.h"
|
||||
#include "pfaedle/trgraph/StatInfo.h"
|
||||
#include "util/geo/GeoGraph.h"
|
||||
|
||||
using util::geograph::GeoNodePL;
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
struct Component {
|
||||
uint8_t minEdgeLvl : 3;
|
||||
};
|
||||
|
||||
/*
|
||||
* A node payload class for the transit graph.
|
||||
*/
|
||||
class NodePL : public GeoNodePL<float> {
|
||||
public:
|
||||
NodePL();
|
||||
NodePL(const NodePL& pl); // NOLINT
|
||||
NodePL(const util::geo::FPoint& geom); // NOLINT
|
||||
NodePL(const util::geo::FPoint& geom, const StatInfo& si);
|
||||
~NodePL();
|
||||
|
||||
// Return the geometry of this node.
|
||||
const util::geo::FPoint* getGeom() const;
|
||||
void setGeom(const util::geo::FPoint& geom);
|
||||
|
||||
// Fill obj with k/v pairs describing the parameters of this payload.
|
||||
void getAttrs(std::map<std::string, std::string>* attrs) const;
|
||||
|
||||
// Set the station info for this node
|
||||
void setSI(const StatInfo& si);
|
||||
|
||||
// Return the station info for this node
|
||||
const StatInfo* getSI() const;
|
||||
StatInfo* getSI();
|
||||
|
||||
// Delete the station info for this node
|
||||
void setNoStat();
|
||||
|
||||
// Get the component of this node
|
||||
const Component* getComp() const;
|
||||
|
||||
// Set the component of this node
|
||||
void setComp(const Component* c);
|
||||
|
||||
// Make this node a blocker
|
||||
void setBlocker();
|
||||
|
||||
// Check if this node is a blocker
|
||||
bool isBlocker() const;
|
||||
|
||||
// Mark this node as visited (usefull for counting search space in Dijkstra)
|
||||
void setVisited() const;
|
||||
|
||||
private:
|
||||
std::string _b;
|
||||
// 32bit floats are enough here
|
||||
util::geo::FPoint _geom;
|
||||
StatInfo* _si;
|
||||
const Component* _component;
|
||||
|
||||
static StatInfo _blockerSI;
|
||||
|
||||
mutable bool _vis;
|
||||
|
||||
static std::unordered_map<const Component*, size_t> _comps;
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_TRGRAPH_NODEPL_H_
|
61
src/pfaedle/trgraph/Normalizer.cpp
Normal file
61
src/pfaedle/trgraph/Normalizer.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "pfaedle/trgraph/Normalizer.h"
|
||||
|
||||
using pfaedle::trgraph::Normalizer;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
Normalizer::Normalizer(const ReplRules& rules) : _rulesOrig(rules) {
|
||||
buildRules(rules);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::string Normalizer::operator()(std::string sn) const {
|
||||
auto i = _cache.find(sn);
|
||||
if (i != _cache.end()) return i->second;
|
||||
|
||||
std::string ret = sn;
|
||||
for (auto rule : _rules) {
|
||||
std::string tmp;
|
||||
std::regex_replace(std::back_inserter(tmp), ret.begin(), ret.end(),
|
||||
rule.first, rule.second,
|
||||
std::regex_constants::format_sed);
|
||||
std::swap(ret, tmp);
|
||||
}
|
||||
|
||||
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
|
||||
|
||||
_cache[sn] = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
bool Normalizer::operator==(const Normalizer& b) const {
|
||||
return _rulesOrig == b._rulesOrig;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void Normalizer::buildRules(const ReplRules& rules) {
|
||||
for (auto rule : rules) {
|
||||
try {
|
||||
_rules.push_back(ReplRuleComp(
|
||||
std::regex(rule.first, std::regex::ECMAScript | std::regex::icase |
|
||||
std::regex::optimize),
|
||||
rule.second));
|
||||
} catch (const std::regex_error& e) {
|
||||
std::stringstream ss;
|
||||
ss << "'" << rule.first << "'"
|
||||
<< ": " << e.what();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
45
src/pfaedle/trgraph/Normalizer.h
Normal file
45
src/pfaedle/trgraph/Normalizer.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_TRGRAPH_NORMALIZER_H_
|
||||
#define PFAEDLE_TRGRAPH_NORMALIZER_H_
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
typedef std::pair<std::string, std::string> ReplRule;
|
||||
typedef std::vector<ReplRule> ReplRules;
|
||||
|
||||
typedef std::pair<std::regex, std::string> ReplRuleComp;
|
||||
typedef std::vector<ReplRuleComp> ReplRulesComp;
|
||||
|
||||
/*
|
||||
* A class for normalizing station names
|
||||
*/
|
||||
class Normalizer {
|
||||
public:
|
||||
Normalizer() {}
|
||||
explicit Normalizer(const ReplRules& rules);
|
||||
|
||||
// Normalize sn based on the rules of this normalizer
|
||||
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;
|
||||
|
||||
void buildRules(const ReplRules& rules);
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_TRGRAPH_NORMALIZER_H_
|
95
src/pfaedle/trgraph/StatGroup.cpp
Normal file
95
src/pfaedle/trgraph/StatGroup.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <set>
|
||||
#include "pfaedle/trgraph/StatGroup.h"
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
using pfaedle::trgraph::StatGroup;
|
||||
using pfaedle::trgraph::Node;
|
||||
using pfaedle::router::NodeCandGroup;
|
||||
using ad::cppgtfs::gtfs::Stop;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
StatGroup::StatGroup() {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatGroup::addStop(const Stop* s) { _stops.insert(s); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatGroup::addNode(trgraph::Node* n) { _nodes.insert(n); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatGroup::merge(StatGroup* other) {
|
||||
if (other == this) return;
|
||||
|
||||
std::set<Node*> nds = other->getNodes();
|
||||
std::set<const Stop*> stops = other->getStops();
|
||||
|
||||
for (auto on : nds) {
|
||||
on->pl().getSI()->setGroup(this);
|
||||
addNode(on);
|
||||
}
|
||||
|
||||
for (auto* os : stops) {
|
||||
addStop(os);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const NodeCandGroup& StatGroup::getNodeCands(const Stop* s) const {
|
||||
return _stopNodePens.at(s);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<Node*>& StatGroup::getNodes() const {
|
||||
return _nodes;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatGroup::remNode(trgraph::Node* n) {
|
||||
auto it = _nodes.find(n);
|
||||
if (it != _nodes.end()) _nodes.erase(it);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
std::set<Node*>& StatGroup::getNodes() { return _nodes; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::set<const Stop*>& StatGroup::getStops() const { return _stops; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double StatGroup::getPen(const Stop* s, trgraph::Node* n,
|
||||
const trgraph::Normalizer& platformNorm,
|
||||
double trackPen, double distPenFac,
|
||||
double nonOsmPen) const {
|
||||
FPoint p = util::geo::latLngToWebMerc<float>(s->getLat(), s->getLng());
|
||||
|
||||
double distPen = util::geo::webMercMeterDist(p, *n->pl().getGeom());
|
||||
distPen *= distPenFac;
|
||||
|
||||
std::string platform = platformNorm(s->getPlatformCode());
|
||||
|
||||
if (!platform.empty() && !n->pl().getSI()->getTrack().empty() &&
|
||||
n->pl().getSI()->getTrack() == platform) {
|
||||
trackPen = 0;
|
||||
}
|
||||
|
||||
if (n->pl().getSI()->isFromOsm()) nonOsmPen = 0;
|
||||
|
||||
return distPen + trackPen + nonOsmPen;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatGroup::writePens(const trgraph::Normalizer& platformNorm,
|
||||
double trackPen, double distPenFac,
|
||||
double nonOsmPen) {
|
||||
if (_stopNodePens.size()) return; // already written
|
||||
for (auto* s : _stops) {
|
||||
for (auto* n : _nodes) {
|
||||
_stopNodePens[s].push_back(router::NodeCand{
|
||||
n, getPen(s, n, platformNorm, trackPen, distPenFac, nonOsmPen)});
|
||||
}
|
||||
}
|
||||
}
|
72
src/pfaedle/trgraph/StatGroup.h
Normal file
72
src/pfaedle/trgraph/StatGroup.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_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_
|
106
src/pfaedle/trgraph/StatInfo.cpp
Normal file
106
src/pfaedle/trgraph/StatInfo.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "pfaedle/router/Comp.h"
|
||||
#include "pfaedle/trgraph/StatGroup.h"
|
||||
#include "pfaedle/trgraph/StatInfo.h"
|
||||
|
||||
using pfaedle::trgraph::StatInfo;
|
||||
using pfaedle::trgraph::StatGroup;
|
||||
|
||||
std::unordered_map<const StatGroup*, size_t> StatInfo::_groups;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
StatInfo::StatInfo() : _name(""), _track(""), _fromOsm(false), _group(0) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
StatInfo::StatInfo(const StatInfo& si)
|
||||
: _name(si._name),
|
||||
_altNames(si._altNames),
|
||||
_track(si._track),
|
||||
_fromOsm(si._fromOsm),
|
||||
_group(0) {
|
||||
setGroup(si._group);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
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; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
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;
|
||||
if (router::statSimi(_name, other->getName()) > 0.5) return 1;
|
||||
|
||||
for (const auto& a : _altNames) {
|
||||
if (router::statSimi(a, other->getName()) > 0.5) return 1;
|
||||
for (const auto& b : other->getAltNames()) {
|
||||
if (router::statSimi(a, b) > 0.5) return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& b : other->getAltNames()) {
|
||||
if (router::statSimi(_name, b) > 0.5) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
const std::vector<std::string>& StatInfo::getAltNames() const {
|
||||
return _altNames;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatInfo::addAltName(const std::string& name) {
|
||||
_altNames.push_back(name);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void StatInfo::setTrack(const std::string& tr) { _track = tr; }
|
71
src/pfaedle/trgraph/StatInfo.h
Normal file
71
src/pfaedle/trgraph/StatInfo.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef PFAEDLE_TRGRAPH_STATINFO_H_
|
||||
#define PFAEDLE_TRGRAPH_STATINFO_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pfaedle {
|
||||
namespace trgraph {
|
||||
|
||||
// forward declaration
|
||||
class StatGroup;
|
||||
|
||||
/*
|
||||
* 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, bool _fromOsm);
|
||||
~StatInfo();
|
||||
|
||||
// Return this stops names.
|
||||
const std::string& getName() const;
|
||||
|
||||
// Return this stops track or empty string, if none.
|
||||
const std::string& getTrack() const;
|
||||
|
||||
// Add an alternative name for this station.
|
||||
void addAltName(const std::string& name);
|
||||
|
||||
// Return all alternative names for this station.
|
||||
const std::vector<std::string>& getAltNames() const;
|
||||
|
||||
// Set the track of this stop.
|
||||
void setTrack(const std::string& tr);
|
||||
|
||||
// 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);
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
std::vector<std::string> _altNames;
|
||||
std::string _track;
|
||||
bool _fromOsm;
|
||||
StatGroup* _group;
|
||||
|
||||
static std::unordered_map<const StatGroup*, size_t> _groups;
|
||||
static void unRefGroup(StatGroup* g);
|
||||
};
|
||||
} // namespace trgraph
|
||||
} // namespace pfaedle
|
||||
|
||||
#endif // PFAEDLE_TRGRAPH_STATINFO_H_
|
5
src/util/CMakeLists.txt
Normal file
5
src/util/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
file(GLOB_RECURSE util_SRC *.cpp)
|
||||
list(REMOVE_ITEM util_SRC TestMain.cpp)
|
||||
add_library(util ${util_SRC})
|
||||
|
||||
add_subdirectory(tests)
|
78
src/util/Misc.h
Normal file
78
src/util/Misc.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_MISC_H_
|
||||
#define UTIL_MISC_H_
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||
#define TIME() std::chrono::high_resolution_clock::now()
|
||||
#define TOOK(t1, t2) (std::chrono::duration_cast<microseconds>(t2 - t1).count() / 1000.0)
|
||||
|
||||
namespace util {
|
||||
|
||||
// cached first 10 powers of 10
|
||||
static int pow10[10] = {
|
||||
1, 10, 100, 1000, 10000,
|
||||
100000, 1000000, 10000000, 100000000, 1000000000};
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline uint64_t factorial(uint64_t n) {
|
||||
if (n == 1) return n;
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline uint64_t atoul(const char* p) {
|
||||
uint64_t ret = 0;
|
||||
|
||||
while (*p) {
|
||||
ret = ret * 10 + (*p++ - '0');
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline float atof(const char* p, uint8_t mn) {
|
||||
// this atof implementation works only on "normal" float strings like
|
||||
// 56.445 or -345.00, but should be faster than std::atof
|
||||
float ret = 0.0;
|
||||
bool neg = false;
|
||||
if (*p == '-') {
|
||||
neg = true;
|
||||
p++;
|
||||
}
|
||||
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
ret = ret * 10.0 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
float f = 0;
|
||||
uint8_t n = 0;
|
||||
|
||||
for (; n < mn && *p >= '0' && *p <= '9'; n++, p++) {
|
||||
f = f * 10.0 + (*p - '0');
|
||||
}
|
||||
|
||||
if (n < 11)
|
||||
ret += f / pow10[n];
|
||||
else
|
||||
ret += f / std::pow(10, n);
|
||||
}
|
||||
|
||||
if (neg) return -ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double atof(const char* p) { return atof(p, 38); }
|
||||
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_MISC_H_
|
114
src/util/Nullable.h
Normal file
114
src/util/Nullable.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_NULLABLE_H_
|
||||
#define UTIL_NULLABLE_H_
|
||||
|
||||
namespace util {
|
||||
|
||||
template<typename T>
|
||||
class Nullable {
|
||||
public:
|
||||
Nullable()
|
||||
: val(), null(true) {}
|
||||
Nullable(T* valPointer)
|
||||
: val(), null(true) {
|
||||
if (valPointer) {
|
||||
assign(*valPointer);
|
||||
}
|
||||
}
|
||||
Nullable(const T& value)
|
||||
: val(value), null(false) {}
|
||||
Nullable(const Nullable& other)
|
||||
: val(other.val), null(other.isNull()) {}
|
||||
|
||||
Nullable& operator=(const Nullable& other) {
|
||||
val = other.get();
|
||||
null = other.isNull();
|
||||
return *this;
|
||||
}
|
||||
|
||||
T operator=(const T& other) {
|
||||
assign(other);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passing through comparision operators
|
||||
*/
|
||||
|
||||
bool operator==(const Nullable& other) const {
|
||||
return (other.isNull() && isNull()) || other.get() == get();
|
||||
}
|
||||
|
||||
bool operator!=(const Nullable& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const Nullable& other) const {
|
||||
return !other.isNull() && !isNull() && get() < other.get();
|
||||
}
|
||||
|
||||
bool operator>(const Nullable& other) const {
|
||||
return !(*this < other || *this == other);
|
||||
}
|
||||
|
||||
bool operator<=(const Nullable& other) const {
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
|
||||
bool operator>=(const Nullable& other) const {
|
||||
return *this > other || *this == other;
|
||||
}
|
||||
|
||||
bool operator==(const T& other) const {
|
||||
return !isNull() && other == get();
|
||||
}
|
||||
|
||||
bool operator!=(const T& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const T& other) const {
|
||||
return !isNull() && get() < other;
|
||||
}
|
||||
|
||||
bool operator>(const T& other) const {
|
||||
return !(*this < other || *this == other);
|
||||
}
|
||||
|
||||
bool operator<=(const T& other) const {
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
|
||||
bool operator>=(const T& other) const {
|
||||
return *this > other || *this == other;
|
||||
}
|
||||
|
||||
operator T() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return null;
|
||||
}
|
||||
|
||||
T get() const {
|
||||
if (!isNull()) return val;
|
||||
else throw std::runtime_error("Trying to retrieve value of NULL object.");
|
||||
}
|
||||
|
||||
private:
|
||||
void assign(T v) {
|
||||
val = v;
|
||||
null = false;
|
||||
}
|
||||
|
||||
T val;
|
||||
bool null;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // UTIL_NULLABLE_H_
|
158
src/util/String.h
Normal file
158
src/util/String.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_STRING_H_
|
||||
#define UTIL_STRING_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace util {
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string urlDecode(const std::string& encoded) {
|
||||
std::string decoded;
|
||||
for (size_t i = 0; i < encoded.size(); ++i) {
|
||||
char c = encoded[i];
|
||||
if (c == '%') {
|
||||
std::string ah = encoded.substr(i + 1, 2);
|
||||
char* nonProced = 0;
|
||||
char hexVal = strtol(ah.c_str(), &nonProced, 16);
|
||||
|
||||
if (ah.find_first_of("+-") > 1 && ah.size() - strlen(nonProced) == 2) {
|
||||
c = hexVal;
|
||||
i += 2;
|
||||
}
|
||||
} else if (c == '+') {
|
||||
c = ' ';
|
||||
}
|
||||
decoded += c;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string jsonStringEscape(const std::string& unescaped) {
|
||||
std::string escaped;
|
||||
for (size_t i = 0; i < unescaped.size(); ++i) {
|
||||
if (unescaped[i] == '"' || unescaped[i] == '\\') {
|
||||
escaped += "\\";
|
||||
}
|
||||
if (iscntrl(unescaped[i])) {
|
||||
escaped += " ";
|
||||
}
|
||||
escaped += unescaped[i];
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline bool replace(std::string& subj, const std::string& from,
|
||||
const std::string& to) {
|
||||
if (from.empty()) return false;
|
||||
size_t start_pos = subj.find(from);
|
||||
if (start_pos != std::string::npos) {
|
||||
subj.replace(start_pos, from.length(), to);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline bool replaceAll(std::string& subj, const std::string& from,
|
||||
const std::string& to) {
|
||||
if (from.empty()) return false;
|
||||
bool found = false;
|
||||
size_t s = subj.find(from, 0);
|
||||
for (; s != std::string::npos; s = subj.find(from, s + to.length())) {
|
||||
found = true;
|
||||
subj.replace(s, from.length(), to);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string unixBasename(const std::string& pathname) {
|
||||
return {std::find_if(pathname.rbegin(), pathname.rend(),
|
||||
[](char c) { return c == '/'; })
|
||||
.base(),
|
||||
pathname.end()};
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline std::string toString(T obj) {
|
||||
std::stringstream ss;
|
||||
ss << obj;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::vector<std::string> split(std::string in, char sep) {
|
||||
std::stringstream ss(in);
|
||||
std::vector<std::string> ret(1);
|
||||
while (std::getline(ss, ret.back(), sep)) {
|
||||
ret.push_back("");
|
||||
}
|
||||
ret.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string ltrim(std::string str) {
|
||||
str.erase(0, str.find_first_not_of(" \t\n\v\f\r"));
|
||||
return str;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string rtrim(std::string str) {
|
||||
str.erase(str.find_last_not_of(" \t\n\v\f\r") + 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline std::string trim(std::string str) { return ltrim(rtrim(str)); }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline size_t editDist(const std::string& s1, const std::string& s2) {
|
||||
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
|
||||
size_t len1 = s1.size();
|
||||
size_t len2 = s2.size();
|
||||
std::vector<size_t> cur(len2 + 1);
|
||||
std::vector<size_t> prev(len2 + 1);
|
||||
|
||||
for (size_t i = 0; i < prev.size(); i++) prev[i] = i;
|
||||
|
||||
for (size_t i = 0; i < len1; i++) {
|
||||
cur[0] = i + 1;
|
||||
for (size_t j = 0; j < len2; j++) {
|
||||
cur[j + 1] =
|
||||
std::min(prev[1 + j] + 1,
|
||||
std::min(cur[j] + 1, prev[j] + (s1[i] == s2[j] ? 0 : 1)));
|
||||
}
|
||||
std::swap(cur, prev);
|
||||
}
|
||||
|
||||
return prev[len2];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline std::string implode(const std::vector<T>& vec, const char* del) {
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < vec.size(); i++) {
|
||||
if (i != 0) ss << del;
|
||||
ss << vec[i];
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_STRING_H_
|
54
src/util/geo/BezierCurve.h
Normal file
54
src/util/geo/BezierCurve.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GEO_BEZIERCURVE_H_
|
||||
#define UTIL_GEO_BEZIERCURVE_H_
|
||||
|
||||
#include <vector>
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/geo/PolyLine.h"
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
|
||||
struct CubicPolynom {
|
||||
CubicPolynom(double a, double b, double c, double d, double x)
|
||||
: a(a), b(b), c(c), d(d), x(x) {}
|
||||
CubicPolynom() : a(0), b(0), c(0), d(0), x(0) {}
|
||||
double a, b, c, d, x;
|
||||
|
||||
double valueAt(double x) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bezier curve
|
||||
*/
|
||||
template <typename T>
|
||||
class BezierCurve {
|
||||
public:
|
||||
BezierCurve(const Point<T>& a, const Point<T>& b, const Point<T>& c, const Point<T>& d);
|
||||
|
||||
const PolyLine<T>& render(double d);
|
||||
|
||||
private:
|
||||
double _d;
|
||||
|
||||
// the x and y polynoms for this spline
|
||||
CubicPolynom _xp, _yp;
|
||||
|
||||
// store the rendered polyline for quicker access
|
||||
PolyLine<T> _rendered;
|
||||
bool _didRender;
|
||||
|
||||
void recalcPolynoms(const Point<T>& x, const Point<T>& b, const Point<T>& c,
|
||||
const Point<T>& d);
|
||||
|
||||
Point<T> valueAt(double t) const;
|
||||
};
|
||||
|
||||
#include "util/geo/PolyLine.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GEO_BEZIERCURVE_H_
|
70
src/util/geo/BezierCurve.tpp
Normal file
70
src/util/geo/BezierCurve.tpp
Normal 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.template get<0>();
|
||||
_xp.b = 3.0 * (b.template get<0>() - a.template get<0>());
|
||||
_xp.c = 3.0 * (c.template get<0>() - b.template get<0>()) - _xp.b;
|
||||
_xp.d = d.template get<0>() - a.template get<0>() - _xp.c - _xp.b;
|
||||
|
||||
_yp.a = a.template get<1>();
|
||||
_yp.b = 3.0 * (b.template get<1>() - a.template get<1>());
|
||||
_yp.c = 3.0 * (c.template get<1>() - b.template get<1>()) - _yp.b;
|
||||
_yp.d = d.template get<1>() - a.template get<1>() - _yp.c - _yp.b;
|
||||
|
||||
_didRender = false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> BezierCurve<T>::valueAt(double t) const {
|
||||
return Point<T>(_xp.valueAt(t), _yp.valueAt(t));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
const PolyLine<T>& BezierCurve<T>::render(double d) {
|
||||
assert(d > 0);
|
||||
if (_didRender) return _rendered;
|
||||
|
||||
if (_d == 0) {
|
||||
_rendered << Point<T>(_xp.a, _yp.a) << Point<T>(_xp.a, _yp.a);
|
||||
return _rendered;
|
||||
}
|
||||
|
||||
_rendered.empty();
|
||||
double n = _d / d, dt = 1 / n, t = 0;
|
||||
|
||||
bool cancel = false;
|
||||
while (true) {
|
||||
_rendered << valueAt(t);
|
||||
t += dt;
|
||||
if (cancel) break;
|
||||
if (t > 1) {
|
||||
t = 1;
|
||||
cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
_didRender = true;
|
||||
return _rendered;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
double CubicPolynom::valueAt(double atx) const {
|
||||
double dx = atx - x;
|
||||
return a + b * dx + c * dx * dx + d * dx * dx * dx;
|
||||
}
|
773
src/util/geo/Geo.h
Normal file
773
src/util/geo/Geo.h
Normal file
|
@ -0,0 +1,773 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
#ifndef UTIL_GEO_GEO_H_
|
||||
#define UTIL_GEO_GEO_H_
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <math.h>
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
|
||||
#include "util/Misc.h"
|
||||
|
||||
// -------------------
|
||||
// Geometry stuff
|
||||
// ------------------
|
||||
|
||||
namespace bgeo = boost::geometry;
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
|
||||
template <typename T>
|
||||
using Point = bgeo::model::point<T, 2, bgeo::cs::cartesian>;
|
||||
|
||||
template <typename T>
|
||||
using Line = bgeo::model::linestring<Point<T>>;
|
||||
|
||||
template <typename T>
|
||||
using MultiLine = bgeo::model::multi_linestring<Line<T>>;
|
||||
|
||||
template <typename T>
|
||||
using Polygon = bgeo::model::polygon<Point<T>>;
|
||||
|
||||
template <typename T>
|
||||
using MultiPolygon = bgeo::model::multi_polygon<Polygon<T>>;
|
||||
|
||||
template <typename T>
|
||||
using Box = bgeo::model::box<Point<T>>;
|
||||
|
||||
// convenience aliases
|
||||
|
||||
typedef Point<double> DPoint;
|
||||
typedef Point<float> FPoint;
|
||||
typedef Point<int> IPoint;
|
||||
|
||||
typedef Line<double> DLine;
|
||||
typedef Line<float> FLine;
|
||||
typedef Line<int> ILine;
|
||||
|
||||
typedef Box<double> DBox;
|
||||
typedef Box<float> FBox;
|
||||
typedef Box<int> IBox;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Line<T> rotate(const Line<T>& geo, double deg, const Point<T>& center) {
|
||||
Line<T> ret;
|
||||
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translate(
|
||||
-center.template get<0>(), -center.template get<1>());
|
||||
bgeo::strategy::transform::rotate_transformer<bgeo::degree, T, 2, 2> rotate(
|
||||
deg);
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translateBack(
|
||||
center.template get<0>(), center.template get<1>());
|
||||
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> translateRotate(
|
||||
prod(rotate.matrix(), translate.matrix()));
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> all(
|
||||
prod(translateBack.matrix(), translateRotate.matrix()));
|
||||
|
||||
bgeo::transform(geo, ret, all);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline MultiLine<T> rotate(const MultiLine<T>& geo, double deg,
|
||||
const Point<T>& center) {
|
||||
MultiLine<T> ret;
|
||||
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translate(
|
||||
-center.template get<0>(), -center.template get<1>());
|
||||
bgeo::strategy::transform::rotate_transformer<bgeo::degree, T, 2, 2> rotate(
|
||||
deg);
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translateBack(
|
||||
center.template get<0>(), center.template get<1>());
|
||||
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> translateRotate(
|
||||
prod(rotate.matrix(), translate.matrix()));
|
||||
bgeo::strategy::transform::ublas_transformer<T, 2, 2> all(
|
||||
prod(translateBack.matrix(), translateRotate.matrix()));
|
||||
|
||||
bgeo::transform(geo, ret, all);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> pad(const Box<T>& box, double padding) {
|
||||
return Box<T>(Point<T>(box.min_corner().template get<0>() - padding,
|
||||
box.min_corner().template get<1>() - padding),
|
||||
Point<T>(box.max_corner().template get<0>() + padding,
|
||||
box.max_corner().template get<1>() + padding));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Line<T> rotate(const Line<T>& geo, double deg) {
|
||||
Point<T> center;
|
||||
bgeo::centroid(geo, center);
|
||||
return rotate(geo, deg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline MultiLine<T> rotate(const MultiLine<T>& geo, double deg) {
|
||||
Point<T> center;
|
||||
bgeo::centroid(geo, center);
|
||||
return rotate(geo, deg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <template <typename> typename Geometry, typename T>
|
||||
inline Geometry<T> move(const Geometry<T>& geo, T x, T y) {
|
||||
Geometry<T> ret;
|
||||
bgeo::strategy::transform::translate_transformer<T, 2, 2> translate(x, y);
|
||||
bgeo::transform(geo, ret, translate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: outfactor
|
||||
|
||||
template <typename T>
|
||||
struct RotatedBox {
|
||||
RotatedBox(const Box<T>& b, double rot, const Point<T>& center)
|
||||
: b(b), rotateDeg(rot), center(center) {}
|
||||
RotatedBox(const Box<T>& b, double rot) : b(b), rotateDeg(rot) {
|
||||
bgeo::centroid(b, center);
|
||||
}
|
||||
|
||||
Box<T> b;
|
||||
double rotateDeg;
|
||||
Point<T> center;
|
||||
|
||||
Polygon<T> getPolygon() {
|
||||
Polygon<T> hull;
|
||||
bgeo::convex_hull(b, hull);
|
||||
return rotate(hull, rotateDeg, center);
|
||||
}
|
||||
};
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> minbox() {
|
||||
return bgeo::make_inverse<Box<T>>();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline RotatedBox<T> shrink(const RotatedBox<T>& b, double d) {
|
||||
double xd =
|
||||
b.b.max_corner().template get<0>() - b.b.min_corner().template get<0>();
|
||||
double yd =
|
||||
b.b.max_corner().template get<1>() - b.b.min_corner().template get<1>();
|
||||
|
||||
if (xd <= 2 * d) d = xd / 2 - 1;
|
||||
if (yd <= 2 * d) d = yd / 2 - 1;
|
||||
|
||||
Box<T> r(Point<T>(b.b.min_corner().template get<0>() + d,
|
||||
b.b.min_corner().template get<1>() + d),
|
||||
Point<T>(b.b.max_corner().template get<0>() - d,
|
||||
b.b.max_corner().template get<1>() - d));
|
||||
|
||||
return RotatedBox<T>(r, b.rotateDeg, b.center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline bool doubleEq(double a, double b) { return fabs(a - b) < 0.000001; }
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry, typename Box>
|
||||
inline bool contains(const Geometry& geom, const Box& box) {
|
||||
return bgeo::within(geom, box);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool contains(const Point<T>& p1, const Point<T>& q1, const Point<T>& p2,
|
||||
const Point<T>& q2) {
|
||||
Line<T> a, b;
|
||||
a.push_back(p1);
|
||||
a.push_back(q1);
|
||||
b.push_back(p2);
|
||||
b.push_back(q2);
|
||||
|
||||
return bgeo::covered_by(a, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool contains(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T q2y) {
|
||||
Point<T> p1(p1x, p1y);
|
||||
Point<T> q1(q1x, q1y);
|
||||
Point<T> p2(p2x, p2y);
|
||||
Point<T> q2(q2x, q2y);
|
||||
|
||||
return contains(p1, q1, p2, q2);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool intersects(const Point<T>& p1, const Point<T>& q1,
|
||||
const Point<T>& p2, const Point<T>& q2) {
|
||||
/*
|
||||
* checks whether two line segments intersect
|
||||
*/
|
||||
Line<T> a, b;
|
||||
a.push_back(p1);
|
||||
a.push_back(q1);
|
||||
b.push_back(p2);
|
||||
b.push_back(q2);
|
||||
|
||||
return !(contains(p1, q1, p2, q2) || contains(p2, q2, p1, q1)) &&
|
||||
bgeo::intersects(a, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool intersects(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T q2y) {
|
||||
/*
|
||||
* checks whether two line segments intersect
|
||||
*/
|
||||
Point<T> p1(p1x, p1y);
|
||||
Point<T> q1(q1x, q1y);
|
||||
Point<T> p2(p2x, p2y);
|
||||
Point<T> q2(q2x, q2y);
|
||||
|
||||
return intersects(p1, q1, p2, q2);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> intersection(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x,
|
||||
T q2y) {
|
||||
/*
|
||||
* calculates the intersection between two line segments
|
||||
*/
|
||||
if (doubleEq(p1x, q1x) && doubleEq(p1y, q1y))
|
||||
return Point<T>(p1x, p1y); // TODO: <-- intersecting with a point??
|
||||
if (doubleEq(p2x, q1x) && doubleEq(p2y, q1y)) return Point<T>(p2x, p2y);
|
||||
if (doubleEq(p2x, q2x) && doubleEq(p2y, q2y))
|
||||
return Point<T>(p2x, p2y); // TODO: <-- intersecting with a point??
|
||||
if (doubleEq(p1x, q2x) && doubleEq(p1y, q2y)) return Point<T>(p1x, p1y);
|
||||
|
||||
double a = ((q2y - p2y) * (q1x - p1x)) - ((q2x - p2x) * (q1y - p1y));
|
||||
double u = (((q2x - p2x) * (p1y - p2y)) - ((q2y - p2y) * (p1x - p2x))) / a;
|
||||
|
||||
return Point<T>(p1x + (q1x - p1x) * u, p1y + (q1y - p1y) * u);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> intersection(const Point<T>& p1, const Point<T>& q1,
|
||||
const Point<T>& p2, const Point<T>& q2) {
|
||||
/*
|
||||
* calculates the intersection between two line segments
|
||||
*/
|
||||
return intersection(p1.template get<0>(), p1.template get<1>(),
|
||||
q1.template get<0>(), q1.template get<1>(),
|
||||
p2.template get<0>(), p2.template get<1>(),
|
||||
q2.template get<0>(), q2.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool lineIntersects(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x,
|
||||
T q2y) {
|
||||
/*
|
||||
* checks whether two lines intersect
|
||||
*/
|
||||
double EPSILON = 0.0000001;
|
||||
double a = ((q2y - p2y) * (q1x - p1x)) - ((q2x - p2x) * (q1y - p1y));
|
||||
|
||||
return a > EPSILON || a < -EPSILON;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline bool lineIntersects(const Point<T>& p1, const Point<T>& q1,
|
||||
const Point<T>& p2, const Point<T>& q2) {
|
||||
/*
|
||||
* checks whether two lines intersect
|
||||
*/
|
||||
return lineIntersects(p1.template get<0>(), p1.template get<1>(),
|
||||
q1.template get<0>(), q1.template get<1>(),
|
||||
p2.template get<0>(), p2.template get<1>(),
|
||||
q2.template get<0>(), q2.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double angBetween(double p1x, double p1y, double q1x, double q1y) {
|
||||
double dY = q1y - p1y;
|
||||
double dX = q1x - p1x;
|
||||
return atan2(dY, dX);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double angBetween(const Point<T>& p1, const Point<T>& q1) {
|
||||
return angBetween(p1.template get<0>(), p1.template get<1>(),
|
||||
q1.template get<0>(), q1.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double dist(double x1, double y1, double x2, double y2) {
|
||||
return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double innerProd(double x1, double y1, double x2, double y2, double x3,
|
||||
double y3) {
|
||||
double dx21 = x2 - x1;
|
||||
double dx31 = x3 - x1;
|
||||
double dy21 = y2 - y1;
|
||||
double dy31 = y3 - y1;
|
||||
double m12 = sqrt(dx21 * dx21 + dy21 * dy21);
|
||||
double m13 = sqrt(dx31 * dx31 + dy31 * dy31);
|
||||
double theta = acos(std::min((dx21 * dx31 + dy21 * dy31) / (m12 * m13), 1.0));
|
||||
|
||||
return theta * (180 / M_PI);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G>
|
||||
inline double innerProd(const G& a, const G& b, const G& c) {
|
||||
return innerProd(a.template get<0>(), a.template get<1>(),
|
||||
b.template get<0>(), b.template get<1>(),
|
||||
c.template get<0>(), c.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename GeometryA, typename GeometryB>
|
||||
inline double dist(const GeometryA& p1, const GeometryB& p2) {
|
||||
return bgeo::distance(p1, p2);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry>
|
||||
inline std::string getWKT(Geometry g) {
|
||||
std::stringstream ss;
|
||||
ss << bgeo::wkt(g);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry>
|
||||
inline double len(Geometry g) {
|
||||
return bgeo::length(g);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename Geometry>
|
||||
inline Geometry simplify(Geometry g, double d) {
|
||||
Geometry ret;
|
||||
bgeo::simplify(g, ret, d);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
inline double distToSegment(double lax, double lay, double lbx, double lby,
|
||||
double px, double py) {
|
||||
double d = dist(lax, lay, lbx, lby) * dist(lax, lay, lbx, lby);
|
||||
if (d == 0) return dist(px, py, lax, lay);
|
||||
|
||||
double t = ((px - lax) * (lbx - lax) + (py - lay) * (lby - lay)) / d;
|
||||
|
||||
if (t < 0) {
|
||||
return dist(px, py, lax, lay);
|
||||
} else if (t > 1) {
|
||||
return dist(px, py, lbx, lby);
|
||||
}
|
||||
|
||||
return dist(px, py, lax + t * (lbx - lax), lay + t * (lby - lay));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double distToSegment(const Point<T>& la, const Point<T>& lb,
|
||||
const Point<T>& p) {
|
||||
return distToSegment(la.template get<0>(), la.template get<1>(),
|
||||
lb.template get<0>(), lb.template get<1>(),
|
||||
p.template get<0>(), p.template get<1>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> projectOn(const Point<T>& a, const Point<T>& b,
|
||||
const Point<T>& c) {
|
||||
if (doubleEq(a.template get<0>(), b.template get<0>()) &&
|
||||
doubleEq(a.template get<1>(), b.template get<1>()))
|
||||
return a;
|
||||
if (doubleEq(a.template get<0>(), c.template get<0>()) &&
|
||||
doubleEq(a.template get<1>(), c.template get<1>()))
|
||||
return a;
|
||||
if (doubleEq(b.template get<0>(), c.template get<0>()) &&
|
||||
doubleEq(b.template get<1>(), c.template get<1>()))
|
||||
return b;
|
||||
|
||||
double x, y;
|
||||
|
||||
if (c.template get<0>() == a.template get<0>()) {
|
||||
// infinite slope
|
||||
x = a.template get<0>();
|
||||
y = b.template get<1>();
|
||||
} else {
|
||||
double m = (double)(c.template get<1>() - a.template get<1>()) /
|
||||
(c.template get<0>() - a.template get<0>());
|
||||
double bb = (double)a.template get<1>() - (m * a.template get<0>());
|
||||
|
||||
x = (m * b.template get<1>() + b.template get<0>() - m * bb) / (m * m + 1);
|
||||
y = (m * m * b.template get<1>() + m * b.template get<0>() + bb) /
|
||||
(m * m + 1);
|
||||
}
|
||||
|
||||
Point<T> ret = Point<T>(x, y);
|
||||
|
||||
bool isBetween = dist(a, c) > dist(a, ret) && dist(a, c) > dist(c, ret);
|
||||
bool nearer = dist(a, ret) < dist(c, ret);
|
||||
|
||||
if (!isBetween) return nearer ? a : c;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double parallelity(const Box<T>& box, const Line<T>& line) {
|
||||
double ret = M_PI;
|
||||
|
||||
double a = angBetween(box.min_corner(),
|
||||
Point<T>(box.min_corner().template get<0>(),
|
||||
box.max_corner().template get<1>()));
|
||||
double b = angBetween(box.min_corner(),
|
||||
Point<T>(box.max_corner().template get<0>(),
|
||||
box.min_corner().template get<1>()));
|
||||
double c = angBetween(box.max_corner(),
|
||||
Point<T>(box.min_corner().template get<0>(),
|
||||
box.max_corner().template get<1>()));
|
||||
double d = angBetween(box.max_corner(),
|
||||
Point<T>(box.max_corner().template get<0>(),
|
||||
box.min_corner().template get<1>()));
|
||||
|
||||
double e = angBetween(line.front(), line.back());
|
||||
|
||||
double vals[] = {a, b, c, d};
|
||||
|
||||
for (double ang : vals) {
|
||||
double v = fabs(ang - e);
|
||||
if (v > M_PI) v = 2 * M_PI - v;
|
||||
if (v > M_PI / 2) v = M_PI - v;
|
||||
if (v < ret) ret = v;
|
||||
}
|
||||
|
||||
return 1 - (ret / (M_PI / 4));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double parallelity(const Box<T>& box, const MultiLine<T>& multiline) {
|
||||
double ret = 0;
|
||||
for (const Line<T>& l : multiline) {
|
||||
ret += parallelity(box, l);
|
||||
}
|
||||
|
||||
return ret / multiline.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename GeomA, typename GeomB>
|
||||
inline bool intersects(const GeomA& a, const GeomB& b) {
|
||||
return bgeo::intersects(a, b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T, template <typename> typename Geometry>
|
||||
inline RotatedBox<T> getOrientedEnvelope(Geometry<T> pol) {
|
||||
// TODO: implement this nicer, works for now, but inefficient
|
||||
// see
|
||||
// https://geidav.wordpress.com/tag/gift-wrapping/#fn-1057-FreemanShapira1975
|
||||
// for a nicer algorithm
|
||||
|
||||
Point<T> center;
|
||||
bgeo::centroid(pol, center);
|
||||
|
||||
Box<T> tmpBox = getBoundingBox(pol);
|
||||
double rotateDeg = 0;
|
||||
|
||||
// rotate in 5 deg steps
|
||||
for (int i = 1; i < 360; i += 1) {
|
||||
pol = rotate(pol, 1, center);
|
||||
Box<T> e;
|
||||
bgeo::envelope(pol, e);
|
||||
if (bgeo::area(tmpBox) > bgeo::area(e)) {
|
||||
tmpBox = e;
|
||||
rotateDeg = i;
|
||||
}
|
||||
}
|
||||
|
||||
return RotatedBox<T>(tmpBox, -rotateDeg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> getBoundingBox(Point<T> pol) {
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
return tmpBox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> getBoundingBox(Line<T> pol) {
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
return tmpBox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> getBoundingBox(Polygon<T> pol) {
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
return tmpBox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Box<T> extendBox(const Box<T>& a, Box<T> b) {
|
||||
bgeo::expand(b, a);
|
||||
return b;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G, typename T>
|
||||
inline Box<T> extendBox(G pol, Box<T> b) {
|
||||
Box<T> tmp;
|
||||
bgeo::envelope(pol, tmp);
|
||||
bgeo::expand(b, tmp);
|
||||
return b;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double commonArea(const Box<T>& ba, const Box<T>& bb) {
|
||||
T l = std::max(ba.min_corner().template get<0>(),
|
||||
bb.min_corner().template get<0>());
|
||||
T r = std::min(ba.max_corner().template get<0>(),
|
||||
bb.max_corner().template get<0>());
|
||||
T b = std::max(ba.min_corner().template get<1>(),
|
||||
bb.min_corner().template get<1>());
|
||||
T t = std::min(ba.max_corner().template get<1>(),
|
||||
bb.max_corner().template get<1>());
|
||||
|
||||
if (l > r || b > t) return 0;
|
||||
|
||||
return (r - l) * (t - b);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T, template <typename> typename Geometry>
|
||||
inline RotatedBox<T> getFullEnvelope(Geometry<T> pol) {
|
||||
Point<T> center;
|
||||
bgeo::centroid(pol, center);
|
||||
|
||||
Box<T> tmpBox;
|
||||
bgeo::envelope(pol, tmpBox);
|
||||
double rotateDeg = 0;
|
||||
|
||||
MultiPolygon<T> ml;
|
||||
|
||||
// rotate in 5 deg steps
|
||||
for (int i = 1; i < 360; i += 1) {
|
||||
pol = rotate(pol, 1, center);
|
||||
Polygon<T> hull;
|
||||
bgeo::convex_hull(pol, hull);
|
||||
ml.push_back(hull);
|
||||
Box<T> e;
|
||||
bgeo::envelope(pol, e);
|
||||
if (bgeo::area(tmpBox) > bgeo::area(e)) {
|
||||
tmpBox = e;
|
||||
rotateDeg = i;
|
||||
}
|
||||
}
|
||||
|
||||
bgeo::envelope(ml, tmpBox);
|
||||
|
||||
return RotatedBox<T>(tmpBox, rotateDeg, center);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline RotatedBox<T> getOrientedEnvelopeAvg(MultiLine<T> ml) {
|
||||
MultiLine<T> orig = ml;
|
||||
// get oriented envelope for hull
|
||||
RotatedBox<T> rbox = getFullEnvelope(ml);
|
||||
Point<T> center;
|
||||
bgeo::centroid(rbox.b, center);
|
||||
|
||||
ml = rotate(ml, -rbox.rotateDeg - 45, center);
|
||||
|
||||
double bestDeg = -45;
|
||||
double score = parallelity(rbox.b, ml);
|
||||
|
||||
for (double i = -45; i <= 45; i += .5) {
|
||||
ml = rotate(ml, -.5, center);
|
||||
double p = parallelity(rbox.b, ml);
|
||||
if (parallelity(rbox.b, ml) > score) {
|
||||
bestDeg = i;
|
||||
score = p;
|
||||
}
|
||||
}
|
||||
|
||||
rbox.rotateDeg += bestDeg;
|
||||
|
||||
// move the box along 45deg angles from its origin until it fits the ml
|
||||
// = until the intersection of its hull and the box is largest
|
||||
Polygon<T> p = rbox.getPolygon();
|
||||
p = rotate(p, -rbox.rotateDeg, rbox.center);
|
||||
|
||||
Polygon<T> hull;
|
||||
bgeo::convex_hull(orig, hull);
|
||||
hull = rotate(hull, -rbox.rotateDeg, rbox.center);
|
||||
|
||||
Box<T> box;
|
||||
bgeo::envelope(hull, box);
|
||||
rbox = RotatedBox<T>(box, rbox.rotateDeg, rbox.center);
|
||||
|
||||
return rbox;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Line<T> densify(const Line<T>& l, double d) {
|
||||
if (!l.size()) return l;
|
||||
|
||||
Line<T> ret;
|
||||
ret.reserve(l.size());
|
||||
ret.push_back(l.front());
|
||||
|
||||
for (size_t i = 1; i < l.size(); i++) {
|
||||
double segd = dist(l[i-1], l[i]);
|
||||
double dx =
|
||||
(l[i].template get<0>() - l[i-1].template get<0>()) / segd;
|
||||
double dy =
|
||||
(l[i].template get<1>() - l[i-1].template get<1>()) / segd;
|
||||
double curd = d;
|
||||
while (curd < segd) {
|
||||
ret.push_back(Point<T>(l[i-1].template get<0>() + dx * curd,
|
||||
l[i-1].template get<1>() + dy * curd));
|
||||
curd += d;
|
||||
}
|
||||
|
||||
ret.push_back(l[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double frechetDistC(size_t i, size_t j, const Line<T>& p,
|
||||
const Line<T>& q,
|
||||
std::vector<std::vector<double>>& ca) {
|
||||
// based on Eiter / Mannila
|
||||
// http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf
|
||||
|
||||
if (ca[i][j] > -1)
|
||||
return ca[i][j];
|
||||
else if (i == 0 && j == 0)
|
||||
ca[i][j] = dist(p[0], q[0]);
|
||||
else if (i > 0 && j == 0)
|
||||
ca[i][j] = std::max(frechetDistC(i - 1, 0, p, q, ca), dist(p[i], q[0]));
|
||||
else if (i == 0 && j > 0)
|
||||
ca[i][j] = std::max(frechetDistC(0, j - 1, p, q, ca), dist(p[0], q[j]));
|
||||
else if (i > 0 && j > 0)
|
||||
ca[i][j] = std::max(std::min(std::min(frechetDistC(i - 1, j, p, q, ca),
|
||||
frechetDistC(i - 1, j - 1, p, q, ca)),
|
||||
frechetDistC(i, j - 1, p, q, ca)),
|
||||
dist(p[i], q[j]));
|
||||
else
|
||||
ca[i][j] = std::numeric_limits<double>::infinity();
|
||||
|
||||
return ca[i][j];
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double frechetDist(const Line<T>& a, const Line<T>& b, double d) {
|
||||
// based on Eiter / Mannila
|
||||
// http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf
|
||||
|
||||
auto p = densify(a, d);
|
||||
auto q = densify(b, d);
|
||||
|
||||
std::vector<std::vector<double>> ca(p.size(),
|
||||
std::vector<double>(q.size(), -1.0));
|
||||
double fd = frechetDistC(p.size() - 1, q.size() - 1, p, q, ca);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline double accFrechetDistC(const Line<T>& a, const Line<T>& b, double d) {
|
||||
|
||||
auto p = densify(a, d);
|
||||
auto q = densify(b, d);
|
||||
|
||||
std::vector<std::vector<double>> ca(p.size(),
|
||||
std::vector<double>(q.size(), 0));
|
||||
|
||||
for (size_t i = 0; i < p.size(); i++) ca[i][0] = std::numeric_limits<double>::infinity();
|
||||
for (size_t j = 0; j < q.size(); j++) ca[0][j] = std::numeric_limits<double>::infinity();
|
||||
ca[0][0] = 0;
|
||||
|
||||
for (size_t i = 1; i < p.size(); i++) {
|
||||
for (size_t j = 1; j < q.size(); j++) {
|
||||
double d = util::geo::dist(p[i], q[j]) * util::geo::dist(p[i], p[i-1]);
|
||||
ca[i][j] = d + std::min(ca[i-1][j], std::min(ca[i][j-1], ca[i-1][j-1]));
|
||||
}
|
||||
}
|
||||
|
||||
return ca[p.size() - 1][q.size() - 1];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> latLngToWebMerc(double lat, double lng) {
|
||||
double x = 6378137.0 * lng * 0.017453292519943295;
|
||||
double a = lat * 0.017453292519943295;
|
||||
|
||||
double y = 3189068.5 * log((1.0 + sin(a)) / (1.0 - sin(a)));
|
||||
return Point<T>(x, y);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
inline Point<T> webMercToLatLng(double x, double y) {
|
||||
double lat = 114.591559026 * (atan(exp(y / 6378137.0)) - 0.78539825);
|
||||
double lon = x / 111319.4907932735677;
|
||||
return Point<T>(lon, lat);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename G1, typename G2>
|
||||
inline double webMercMeterDist(const G1& a, const G2& b) {
|
||||
// euclidean distance on web mercator is in meters on equator,
|
||||
// and proportional to cos(lat) in both y directions
|
||||
|
||||
double latA = 2 * atan(exp(a.template get<1>() / 6378137.0)) - 1.5707965;
|
||||
double latB = 2 * atan(exp(b.template get<1>() / 6378137.0)) - 1.5707965;
|
||||
|
||||
return util::geo::dist(a, b) * cos((latA + latB) / 2.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GEO_GEO_H_
|
31
src/util/geo/GeoGraph.h
Normal file
31
src/util/geo/GeoGraph.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GEOGRAPH_H_
|
||||
#define UTIL_GEOGRAPH_H_
|
||||
|
||||
#include <map>
|
||||
#include "util/geo/Geo.h"
|
||||
|
||||
namespace util {
|
||||
namespace geograph {
|
||||
|
||||
template<typename T>
|
||||
class GeoEdgePL {
|
||||
public:
|
||||
virtual const util::geo::Line<T>* getGeom() const = 0;
|
||||
virtual void getAttrs(std::map<std::string, std::string>* m) const = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class GeoNodePL {
|
||||
public:
|
||||
virtual const util::geo::Point<T>* getGeom() const = 0;
|
||||
virtual void getAttrs(std::map<std::string, std::string>* m) const = 0;
|
||||
};
|
||||
|
||||
} // namespace geograph
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GEOGRAPH_H_
|
88
src/util/geo/Grid.h
Normal file
88
src/util/geo/Grid.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
// 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 "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> typename 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
228
src/util/geo/Grid.tpp
Normal 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> typename 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> typename G, typename T>
|
||||
Grid<V, G, T>::Grid() : Grid<V, G, T>(true) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> typename 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> typename 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.max_corner().template get<0>() - bbox.min_corner().template get<0>();
|
||||
_height =
|
||||
bbox.max_corner().template get<1>() - bbox.min_corner().template get<1>();
|
||||
|
||||
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> typename G, typename T>
|
||||
void Grid<V, G, T>::add(G<T> geom, V val) {
|
||||
Box<T> box = getBoundingBox(geom);
|
||||
size_t swX = getCellXFromX(box.min_corner().template get<0>());
|
||||
size_t swY = getCellYFromY(box.min_corner().template get<1>());
|
||||
|
||||
size_t neX = getCellXFromX(box.max_corner().template get<0>());
|
||||
size_t neY = getCellYFromY(box.max_corner().template get<1>());
|
||||
|
||||
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> typename 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> typename G, typename T>
|
||||
void Grid<V, G, T>::get(const Box<T>& box, std::set<V>* s) const {
|
||||
size_t swX = getCellXFromX(box.min_corner().template get<0>());
|
||||
size_t swY = getCellYFromY(box.min_corner().template get<1>());
|
||||
|
||||
size_t neX = getCellXFromX(box.max_corner().template get<0>());
|
||||
size_t neY = getCellYFromY(box.max_corner().template get<1>());
|
||||
|
||||
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> typename 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.min_corner().template get<0>() - d,
|
||||
a.min_corner().template get<1>() - d),
|
||||
Point<T>(a.max_corner().template get<0>() + d,
|
||||
a.max_corner().template get<1>() + d));
|
||||
return get(b, s);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> typename 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> typename 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> typename 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> typename 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> typename 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> typename 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> typename G, typename T>
|
||||
Box<T> Grid<V, G, T>::getBox(size_t x, size_t y) const {
|
||||
Point<T> sw(_bb.min_corner().template get<0>() + x * _cellWidth,
|
||||
_bb.min_corner().template get<1>() + y * _cellHeight);
|
||||
Point<T> ne(_bb.min_corner().template get<0>() + (x + 1) * _cellWidth,
|
||||
_bb.min_corner().template get<1>() + (y + 1) * _cellHeight);
|
||||
return Box<T>(sw, ne);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> typename G, typename T>
|
||||
size_t Grid<V, G, T>::getCellXFromX(double x) const {
|
||||
float dist = x - _bb.min_corner().template get<0>();
|
||||
if (dist < 0) dist = 0;
|
||||
return floor(dist / _cellWidth);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> typename G, typename T>
|
||||
size_t Grid<V, G, T>::getCellYFromY(double y) const {
|
||||
float dist = y - _bb.min_corner().template get<1>();
|
||||
if (dist < 0) dist = 0;
|
||||
return floor(dist / _cellHeight);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> typename G, typename T>
|
||||
size_t Grid<V, G, T>::getXWidth() const {
|
||||
return _xWidth;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename V, template <typename> typename G, typename T>
|
||||
size_t Grid<V, G, T>::getYHeight() const {
|
||||
return _yHeight;
|
||||
}
|
139
src/util/geo/PolyLine.h
Normal file
139
src/util/geo/PolyLine.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GEO_POLYLINE_H_
|
||||
#define UTIL_GEO_POLYLINE_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Geo.h"
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
|
||||
static const double MAX_EQ_DISTANCE = 15;
|
||||
static const double AVERAGING_STEP = 20;
|
||||
|
||||
template <typename T>
|
||||
struct LinePoint {
|
||||
LinePoint() : lastIndex(0), totalPos(-1), p() {}
|
||||
|
||||
LinePoint(size_t i, double pos, const Point<T>& p)
|
||||
: lastIndex(i), totalPos(pos), p(p) {}
|
||||
size_t lastIndex;
|
||||
double totalPos;
|
||||
Point<T> p;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct LinePointCmp {
|
||||
bool operator()(const LinePoint<T>& lh, const LinePoint<T>& rh) const {
|
||||
return lh.totalPos < rh.totalPos;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
using LinePointPair = std::pair<LinePoint<T>, LinePoint<T>>;
|
||||
template <typename T>
|
||||
using SharedSegment = std::pair<LinePointPair<T>, LinePointPair<T>>;
|
||||
|
||||
template <typename T>
|
||||
struct SharedSegments {
|
||||
std::vector<SharedSegment<T>> segments;
|
||||
};
|
||||
|
||||
// TODO: maybe let this class inherit from a more generic geometry class
|
||||
template <typename T>
|
||||
class PolyLine {
|
||||
public:
|
||||
PolyLine();
|
||||
PolyLine(const Point<T>& from, const Point<T>& to);
|
||||
PolyLine(const Line<T>& l);
|
||||
|
||||
PolyLine& operator<<(const Point<T>& p);
|
||||
PolyLine& operator>>(const Point<T>& p);
|
||||
|
||||
void reverse();
|
||||
PolyLine getReversed() const;
|
||||
|
||||
void offsetPerp(double units);
|
||||
|
||||
PolyLine getPerpOffsetted(double units) const;
|
||||
|
||||
const Line<T>& getLine() const;
|
||||
|
||||
double distTo(const PolyLine<T>& g) const;
|
||||
double distTo(const Point<T>& p) const;
|
||||
|
||||
SharedSegments<T> getSharedSegments(const PolyLine<T>& pl, double dmax) const;
|
||||
|
||||
double getLength() const;
|
||||
|
||||
// return point at dist
|
||||
LinePoint<T> getPointAtDist(double dist) const;
|
||||
|
||||
// return point at [0..1]
|
||||
LinePoint<T> getPointAt(double dist) const;
|
||||
|
||||
PolyLine<T> getSegment(double a, double b) const;
|
||||
PolyLine<T> getSegmentAtDist(double dista, double distb) const;
|
||||
PolyLine<T> getSegment(const LinePoint<T>& start, const LinePoint<T>& end) const;
|
||||
PolyLine<T> getSegment(const Point<T>& a, const Point<T>& b) const;
|
||||
|
||||
std::set<LinePoint<T>, LinePointCmp<T>> getIntersections(const PolyLine<T>& g) const;
|
||||
|
||||
static PolyLine<T> average(const std::vector<const PolyLine<T>*>& lines);
|
||||
static PolyLine<T> average(const std::vector<const PolyLine<T>*>& lines,
|
||||
const std::vector<double>& weights);
|
||||
|
||||
void simplify(double d);
|
||||
void empty();
|
||||
|
||||
void smoothenOutliers(double d);
|
||||
|
||||
std::pair<size_t, double> nearestSegment(const Point<T>& p) const;
|
||||
std::pair<size_t, double> nearestSegmentAfter(const Point<T>& p,
|
||||
size_t after) const;
|
||||
|
||||
LinePoint<T> projectOn(const Point<T>& p) const;
|
||||
LinePoint<T> projectOnAfter(const Point<T>& p, size_t after) const;
|
||||
|
||||
void move(double vx, double vy);
|
||||
|
||||
std::pair<double, double> getSlopeBetween(double ad, double bd) const;
|
||||
std::pair<double, double> getSlopeBetweenDists(double ad, double bd) const;
|
||||
|
||||
// equality operator, will hold frechet-distance equality check in
|
||||
// the dmax
|
||||
bool operator==(const PolyLine& rhs) const;
|
||||
bool contains(const PolyLine& rhs, double dmax) const;
|
||||
bool equals(const PolyLine& rhs) const;
|
||||
bool equals(const PolyLine& rhs, double dmax) const;
|
||||
|
||||
std::string getWKT() const;
|
||||
|
||||
PolyLine getOrthoLineAtDist(double d, double lengt) const;
|
||||
|
||||
Point<T> interpolate(const Point<T>& a, const Point<T>& b, double p) const;
|
||||
|
||||
void fixTopology(double maxl);
|
||||
void applyChaikinSmooth(size_t depth);
|
||||
|
||||
const Point<T>& front() const;
|
||||
const Point<T>& back() const;
|
||||
|
||||
private:
|
||||
std::set<LinePoint<T>, LinePointCmp<T>> getIntersections(const PolyLine& p,
|
||||
size_t a, size_t b) const;
|
||||
Line<T> _line;
|
||||
};
|
||||
|
||||
#include "util/geo/PolyLine.tpp"
|
||||
|
||||
} // namespace geo
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_GEO_POLYLINE_H_
|
739
src/util/geo/PolyLine.tpp
Normal file
739
src/util/geo/PolyLine.tpp
Normal file
|
@ -0,0 +1,739 @@
|
|||
// Copyright 2016, University of Freibur
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/geo/PolyLine.h"
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T>::PolyLine() {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T>::PolyLine(const Point<T>& from, const Point<T>& to) {
|
||||
*this << from << to;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T>::PolyLine(const Line<T>& l) : _line(l) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T>& PolyLine<T>::operator<<(const Point<T>& p) {
|
||||
_line.push_back(p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T>& PolyLine<T>::operator>>(const Point<T>& p) {
|
||||
_line.insert(_line.begin(), p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::reverse() {
|
||||
std::reverse(_line.begin(), _line.end());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getReversed() const {
|
||||
PolyLine ret = *this;
|
||||
ret.reverse();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
const Line<T>& PolyLine<T>::getLine() const {
|
||||
return _line;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getPerpOffsetted(double units) const {
|
||||
PolyLine p = *this;
|
||||
p.offsetPerp(units);
|
||||
return p;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::offsetPerp(double units) {
|
||||
/*
|
||||
* calculate perpendicular offset of a polyline
|
||||
*
|
||||
* there doesn't seem to be any library which reliably does that,
|
||||
* so we do it ourself here until we find one...
|
||||
* boost::geometry only supports buffering a line, resulting in a
|
||||
* polygon. An offsetted line is part of that polygon, but retrieving
|
||||
* it reliably could result in some geometrical diffing hocus pocus which is
|
||||
* bound to go wrong at /some/ point (self intersections, numerical
|
||||
* instability etc)
|
||||
*/
|
||||
|
||||
if (fabs(units) < 0.001) return;
|
||||
|
||||
assert(getLength() > 0);
|
||||
|
||||
if (_line.size() < 2) return;
|
||||
|
||||
Line<T> ret;
|
||||
Point<T> lastP = _line.front();
|
||||
|
||||
Point<T> *lastIns = 0, *befLastIns = 0;
|
||||
|
||||
for (size_t i = 1; i < _line.size(); i++) {
|
||||
Point<T> curP = _line[i];
|
||||
|
||||
double n1 = lastP.template get<1>() - curP.template get<1>();
|
||||
double n2 = curP.template get<0>() - lastP.template get<0>();
|
||||
double n = sqrt(n1 * n1 + n2 * n2);
|
||||
|
||||
// if n == 0, the segment is effectively a point
|
||||
// we would get into all sorts of troubles if we tried to offset a point...
|
||||
if (!(n > 0)) continue;
|
||||
|
||||
n1 = n1 / n;
|
||||
n2 = n2 / n;
|
||||
|
||||
lastP.set<0>(lastP.template get<0>() + (n1 * units));
|
||||
lastP.set<1>(lastP.template get<1>() + (n2 * units));
|
||||
|
||||
curP.set<0>(curP.template get<0>() + (n1 * units));
|
||||
curP.set<1>(curP.template get<1>() + (n2 * units));
|
||||
|
||||
if (lastIns && befLastIns &&
|
||||
lineIntersects(*lastIns, *befLastIns, lastP, curP)) {
|
||||
*lastIns = intersection(*lastIns, *befLastIns, lastP, curP);
|
||||
|
||||
double d = dist(lastP, *lastIns);
|
||||
double d2 = distToSegment(*lastIns, *befLastIns, lastP);
|
||||
|
||||
if (d > fabs(units) * 2 && d2 < d - (fabs(units))) {
|
||||
PolyLine pl(*lastIns, *befLastIns);
|
||||
PolyLine pll(*lastIns, curP);
|
||||
pl = pl.getSegment(0, (d - (fabs(units))) / pl.getLength());
|
||||
pll = pll.getSegment(0, (d - (fabs(units))) / pll.getLength());
|
||||
|
||||
ret.push_back(pll.back());
|
||||
*lastIns = pl.back();
|
||||
|
||||
ret.push_back(curP);
|
||||
} else {
|
||||
ret.push_back(curP);
|
||||
}
|
||||
} else {
|
||||
ret.push_back(lastP);
|
||||
ret.push_back(curP);
|
||||
}
|
||||
|
||||
lastIns = &ret[ret.size() - 1];
|
||||
befLastIns = &ret[ret.size() - 2];
|
||||
|
||||
lastP = _line[i];
|
||||
}
|
||||
|
||||
_line = ret;
|
||||
|
||||
// heuristics
|
||||
simplify(1);
|
||||
fixTopology(fabs(2 * 3.14 * units));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getSegment(double a, double b) const {
|
||||
if (a > b) {
|
||||
double c = a;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
LinePoint<T> start = getPointAt(a);
|
||||
LinePoint<T> end = getPointAt(b);
|
||||
|
||||
return getSegment(start, end);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getSegmentAtDist(double a, double b) const {
|
||||
if (a > b) {
|
||||
double c = a;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
LinePoint<T> start = getPointAtDist(a);
|
||||
LinePoint<T> end = getPointAtDist(b);
|
||||
|
||||
return getSegment(start, end);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getSegment(const Point<T>& a,
|
||||
const Point<T>& b) const {
|
||||
LinePoint<T> start = projectOn(a);
|
||||
LinePoint<T> end = projectOnAfter(b, start.lastIndex);
|
||||
|
||||
return getSegment(start, end);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getSegment(const LinePoint<T>& start,
|
||||
const LinePoint<T>& end) const {
|
||||
PolyLine ret;
|
||||
ret << start.p;
|
||||
|
||||
if (start.lastIndex + 1 <= end.lastIndex) {
|
||||
ret._line.insert(ret._line.end(), _line.begin() + start.lastIndex + 1,
|
||||
_line.begin() + end.lastIndex + 1);
|
||||
}
|
||||
ret << end.p;
|
||||
|
||||
// find a more performant way to clear the result of above
|
||||
ret.simplify(0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
LinePoint<T> PolyLine<T>::getPointAtDist(double atDist) const {
|
||||
if (atDist > getLength()) atDist = getLength();
|
||||
if (atDist < 0) atDist = 0;
|
||||
|
||||
double dist = 0;
|
||||
|
||||
if (_line.size() == 1) return LinePoint<T>(0, 0, _line[0]);
|
||||
|
||||
const Point<T>* last = &_line[0];
|
||||
|
||||
for (size_t i = 1; i < _line.size(); i++) {
|
||||
const Point<T>& cur = _line[i];
|
||||
double d = geo::dist(last, cur);
|
||||
dist += d;
|
||||
|
||||
if (dist > atDist) {
|
||||
double p = (d - (dist - atDist));
|
||||
return LinePoint<T>(i - 1, atDist / getLength(),
|
||||
interpolate(*last, cur, p));
|
||||
}
|
||||
|
||||
last = &_line[i];
|
||||
}
|
||||
|
||||
return LinePoint<T>(_line.size() - 1, 1, _line.back());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
LinePoint<T> PolyLine<T>::getPointAt(double at) const {
|
||||
at *= getLength();
|
||||
return getPointAtDist(at);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Point<T> PolyLine<T>::interpolate(const Point<T>& a, const Point<T>& b,
|
||||
double p) const {
|
||||
double n1 = b.template get<0>() - a.template get<0>();
|
||||
double n2 = b.template get<1>() - a.template get<1>();
|
||||
double n = sqrt(n1 * n1 + n2 * n2);
|
||||
n1 = n1 / n;
|
||||
n2 = n2 / n;
|
||||
return Point<T>(a.template get<0>() + (n1 * p),
|
||||
a.template get<1>() + (n2 * p));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
double PolyLine<T>::distTo(const PolyLine<T>& g) const {
|
||||
return dist(_line, g.getLine());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
double PolyLine<T>::distTo(const Point<T>& p) const {
|
||||
return dist(_line, p);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
double PolyLine<T>::getLength() const {
|
||||
return len(_line);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::average(const std::vector<const PolyLine<T>*>& lines,
|
||||
const std::vector<double>& weights) {
|
||||
bool weighted = lines.size() == weights.size();
|
||||
double stepSize;
|
||||
|
||||
double longestLength = DBL_MIN; // avoid recalc of length on each comparision
|
||||
for (const PolyLine* p : lines) {
|
||||
if (p->getLength() > longestLength) {
|
||||
longestLength = p->getLength();
|
||||
}
|
||||
}
|
||||
|
||||
PolyLine ret;
|
||||
double total = 0;
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
if (weighted) {
|
||||
total += weights[i];
|
||||
} else {
|
||||
total += 1;
|
||||
}
|
||||
}
|
||||
|
||||
stepSize = AVERAGING_STEP / longestLength;
|
||||
bool end = false;
|
||||
for (double a = 0; !end; a += stepSize) {
|
||||
if (a > 1) {
|
||||
a = 1;
|
||||
end = true;
|
||||
}
|
||||
double x = 0, y = 0;
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
const PolyLine* pl = lines[i];
|
||||
Point<T> p = pl->getPointAt(a).p;
|
||||
if (weighted) {
|
||||
x += p.template get<0>() * weights[i];
|
||||
y += p.template get<1>() * weights[i];
|
||||
} else {
|
||||
x += p.template get<0>();
|
||||
y += p.template get<1>();
|
||||
}
|
||||
}
|
||||
ret << Point<T>(x / total, y / total);
|
||||
}
|
||||
|
||||
ret.simplify(0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::average(const std::vector<const PolyLine<T>*>& lines) {
|
||||
return average(lines, std::vector<double>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::pair<size_t, double> PolyLine<T>::nearestSegmentAfter(const Point<T>& p,
|
||||
size_t a) const {
|
||||
// returns the index of the starting point of the nearest segment of p
|
||||
assert(a < _line.size());
|
||||
|
||||
double totalLength = getLength();
|
||||
size_t smallest = a;
|
||||
double totalDist = 0;
|
||||
double dist = DBL_MAX;
|
||||
double smallestDist = 0;
|
||||
|
||||
for (size_t i = smallest + 1; i < _line.size(); i++) {
|
||||
Point<T> startP(_line[i - 1]);
|
||||
Point<T> endP(_line[i]);
|
||||
|
||||
if (i > 1) {
|
||||
totalDist += geo::dist(_line[i - 2], _line[i - 1]);
|
||||
}
|
||||
|
||||
double curDist = distToSegment(startP, endP, p);
|
||||
|
||||
if (curDist < dist) {
|
||||
dist = curDist;
|
||||
smallest = i - 1;
|
||||
smallestDist = totalDist;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalLength > 0) {
|
||||
smallestDist /= totalLength;
|
||||
} else {
|
||||
smallestDist = 0;
|
||||
}
|
||||
|
||||
return std::pair<size_t, double>(smallest, smallestDist);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::pair<size_t, double> PolyLine<T>::nearestSegment(const Point<T>& p) const {
|
||||
return nearestSegmentAfter(p, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
LinePoint<T> PolyLine<T>::projectOn(const Point<T>& p) const {
|
||||
return projectOnAfter(p, 0);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
LinePoint<T> PolyLine<T>::projectOnAfter(const Point<T>& p, size_t a) const {
|
||||
assert(a < _line.size());
|
||||
std::pair<size_t, double> bc = nearestSegmentAfter(p, a);
|
||||
|
||||
Point<T> ret = geo::projectOn(_line[bc.first], p, _line[bc.first + 1]);
|
||||
|
||||
if (getLength() > 0) {
|
||||
bc.second += dist(_line[bc.first], ret) / getLength();
|
||||
}
|
||||
|
||||
return LinePoint<T>(bc.first, bc.second, ret);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::simplify(double d) {
|
||||
_line = geo::simplify(_line, d);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::smoothenOutliers(double d) {
|
||||
if (_line.size() < 3) return;
|
||||
for (size_t i = 1; i < _line.size() - 3; ++i) {
|
||||
double ang = innerProd(_line[i], _line[i - 1], _line[i + 1]);
|
||||
|
||||
if (dist(_line[i], _line[i + 1]) < d || dist(_line[i], _line[i - 1]) < d) {
|
||||
if (ang < 35) {
|
||||
_line.erase(_line.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
bool PolyLine<T>::equals(const PolyLine<T>& rhs) const {
|
||||
// TODO: why 100? make global static or configurable or determine in some
|
||||
// way!
|
||||
return equals(rhs, 100);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
bool PolyLine<T>::operator==(const PolyLine<T>& rhs) const {
|
||||
// TODO: why 100? make global static or configurable or determine in some
|
||||
// way!
|
||||
return equals(rhs, 100);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
bool PolyLine<T>::equals(const PolyLine<T>& rhs, double dmax) const {
|
||||
// check if two lines are equal, THE DIRECTION DOES NOT MATTER HERE!!!!!
|
||||
|
||||
if (_line.size() == 2 && _line.size() == rhs.getLine().size()) {
|
||||
// trivial case, straight line, implement directly
|
||||
return (dist(_line[0], rhs.getLine()[0]) < dmax &&
|
||||
dist(_line.back(), rhs.back()) < dmax) ||
|
||||
(dist(_line[0], rhs.back()) < dmax &&
|
||||
dist(_line.back(), rhs.getLine()[0]) < dmax);
|
||||
} else {
|
||||
return contains(rhs, dmax) && rhs.contains(*this, dmax);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
bool PolyLine<T>::contains(const PolyLine<T>& rhs, double dmax) const {
|
||||
// check if two lines are equal. Line direction does not matter here.
|
||||
|
||||
for (size_t i = 0; i < rhs.getLine().size(); ++i) {
|
||||
double d = dist(rhs.getLine()[i], getLine());
|
||||
if (d > dmax) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::move(double vx, double vy) {
|
||||
for (size_t i = 0; i < _line.size(); i++) {
|
||||
_line[i].set<0>(_line[i].template get<0>() + vx);
|
||||
_line[i].set<1>(_line[i].template get<1>() + vy);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
SharedSegments<T> PolyLine<T>::getSharedSegments(const PolyLine<T>& pl,
|
||||
double dmax) const {
|
||||
/**
|
||||
* Returns the segments this polyline share with pl
|
||||
* atm, this is a very simple distance-based algorithm
|
||||
*/
|
||||
double STEP_SIZE = 2;
|
||||
double MAX_SKIPS = 4;
|
||||
double MIN_SEG_LENGTH = 1; // dmax / 2; // make this configurable!
|
||||
|
||||
SharedSegments<T> ret;
|
||||
|
||||
if (distTo(pl) > dmax) return ret;
|
||||
|
||||
bool in = false, single = true;
|
||||
double curDist = 0;
|
||||
double curTotalSegDist = 0;
|
||||
size_t skips;
|
||||
|
||||
LinePoint<T> curStartCand, curEndCand, curStartCandCmp, curEndCandCmp;
|
||||
|
||||
double comp = 0, curSegDist = 0;
|
||||
double length = getLength(), plLength = pl.getLength();
|
||||
|
||||
for (size_t i = 1; i < _line.size(); ++i) {
|
||||
const Point<T>& s = _line[i - 1];
|
||||
const Point<T>& e = _line[i];
|
||||
|
||||
bool lastRound = false;
|
||||
|
||||
double totalDist = dist(s, e);
|
||||
while (curSegDist <= totalDist) {
|
||||
const Point<T>& curPointer = interpolate(s, e, curSegDist);
|
||||
|
||||
if (pl.distTo(curPointer) <= dmax) {
|
||||
LinePoint<T> curCmpPointer = pl.projectOn(curPointer);
|
||||
LinePoint<T> curBackProjectedPointer = projectOn(curCmpPointer.p);
|
||||
skips = 0;
|
||||
|
||||
if (in) {
|
||||
curEndCand = curBackProjectedPointer;
|
||||
curEndCandCmp = curCmpPointer;
|
||||
|
||||
single = false;
|
||||
|
||||
comp = fabs(curStartCand.totalPos * length -
|
||||
curEndCand.totalPos * length) /
|
||||
fabs(curStartCandCmp.totalPos * plLength -
|
||||
curEndCandCmp.totalPos * plLength);
|
||||
} else {
|
||||
in = true;
|
||||
curStartCand = curBackProjectedPointer;
|
||||
curStartCandCmp = curCmpPointer;
|
||||
}
|
||||
} else {
|
||||
if (in) {
|
||||
skips++;
|
||||
if (skips > MAX_SKIPS) { // TODO: make configurable
|
||||
if (comp > 0.8 && comp < 1.2 && !single &&
|
||||
(fabs(curStartCand.totalPos * length -
|
||||
curEndCand.totalPos * length) > MIN_SEG_LENGTH &&
|
||||
fabs(curStartCandCmp.totalPos * plLength -
|
||||
curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) {
|
||||
ret.segments.push_back(
|
||||
SharedSegment<T>(std::pair<LinePoint<T>, LinePoint<T>>(
|
||||
curStartCand, curStartCandCmp),
|
||||
std::pair<LinePoint<T>, LinePoint<T>>(
|
||||
curEndCand, curEndCandCmp)));
|
||||
|
||||
// TODO: only return the FIRST one, make this configuralbe
|
||||
return ret;
|
||||
}
|
||||
|
||||
in = false;
|
||||
single = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (curSegDist + STEP_SIZE > totalDist && !lastRound) {
|
||||
lastRound = true;
|
||||
double finalStep = totalDist - curSegDist - 0.0005;
|
||||
curSegDist += finalStep;
|
||||
curDist += finalStep;
|
||||
} else {
|
||||
curSegDist += STEP_SIZE;
|
||||
curDist += STEP_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
curSegDist = curSegDist - totalDist;
|
||||
curTotalSegDist += totalDist;
|
||||
}
|
||||
|
||||
if (comp > 0.8 && comp < 1.2 && in && !single &&
|
||||
(fabs(curStartCand.totalPos * length - curEndCand.totalPos * length) >
|
||||
MIN_SEG_LENGTH &&
|
||||
fabs(curStartCandCmp.totalPos * plLength -
|
||||
curEndCandCmp.totalPos * plLength) > MIN_SEG_LENGTH)) {
|
||||
ret.segments.push_back(SharedSegment<T>(
|
||||
std::pair<LinePoint<T>, LinePoint<T>>(curStartCand, curStartCandCmp),
|
||||
std::pair<LinePoint<T>, LinePoint<T>>(curEndCand, curEndCandCmp)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::set<LinePoint<T>, LinePointCmp<T>> PolyLine<T>::getIntersections(
|
||||
const PolyLine<T>& g) const {
|
||||
std::set<LinePoint<T>, LinePointCmp<T>> ret;
|
||||
|
||||
for (size_t i = 1; i < g.getLine().size(); ++i) {
|
||||
// for each line segment, check if it intersects with a line segment in g
|
||||
const std::set<LinePoint<T>, LinePointCmp<T>> a =
|
||||
getIntersections(g, i - 1, i);
|
||||
ret.insert(a.begin(), a.end());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::set<LinePoint<T>, LinePointCmp<T>> PolyLine<T>::getIntersections(
|
||||
const PolyLine<T>& p, size_t a, size_t b) const {
|
||||
std::set<LinePoint<T>, LinePointCmp<T>> ret;
|
||||
|
||||
if (dist(p.getLine()[a], p.getLine()[b]) == 0) {
|
||||
// we cannot intersect with a point
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < _line.size(); ++i) {
|
||||
if (intersects(_line[i - 1], _line[i], p.getLine()[a], p.getLine()[b])) {
|
||||
Point<T> isect =
|
||||
intersection(_line[i - 1], _line[i], p.getLine()[a], p.getLine()[b]);
|
||||
ret.insert(p.projectOn(isect));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
PolyLine<T> PolyLine<T>::getOrthoLineAtDist(double d, double length) const {
|
||||
Point<T> avgP = getPointAtDist(d).p;
|
||||
|
||||
double angle = angBetween(getPointAtDist(d - 5).p, getPointAtDist(d + 5).p);
|
||||
|
||||
double angleX1 = avgP.template get<0>() + cos(angle + M_PI / 2) * length / 2;
|
||||
double angleY1 = avgP.template get<1>() + sin(angle + M_PI / 2) * length / 2;
|
||||
|
||||
double angleX2 = avgP.template get<0>() + cos(angle + M_PI / 2) * -length / 2;
|
||||
double angleY2 = avgP.template get<1>() + sin(angle + M_PI / 2) * -length / 2;
|
||||
|
||||
return PolyLine(Point<T>(angleX1, angleY1), Point<T>(angleX2, angleY2));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::empty() {
|
||||
_line.empty();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::pair<double, double> PolyLine<T>::getSlopeBetween(double ad,
|
||||
double bd) const {
|
||||
LinePoint<T> a = getPointAt(ad);
|
||||
LinePoint<T> b = getPointAt(bd);
|
||||
|
||||
double d = dist(a.p, b.p);
|
||||
|
||||
double dx = (b.p.template get<0>() - a.p.template get<0>()) / d;
|
||||
double dy = (b.p.template get<1>() - a.p.template get<1>()) / d;
|
||||
|
||||
return std::pair<double, double>(dx, dy);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::pair<double, double> PolyLine<T>::getSlopeBetweenDists(double ad,
|
||||
double bd) const {
|
||||
return getSlopeBetween(ad / getLength(), bd / getLength());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
std::string PolyLine<T>::getWKT() const {
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(12) << geo::getWKT(_line);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::fixTopology(double maxl) {
|
||||
double distA = 0;
|
||||
|
||||
for (size_t i = 1; i < _line.size() - 1; i++) {
|
||||
double distB =
|
||||
distA + dist(_line[i - 1], _line[i]) + dist(_line[i], _line[i + 1]);
|
||||
for (size_t j = i + 2; j < _line.size(); j++) {
|
||||
if (intersects(_line[i - 1], _line[i], _line[j - 1], _line[j])) {
|
||||
Point<T> p =
|
||||
intersection(_line[i - 1], _line[i], _line[j - 1], _line[j]);
|
||||
|
||||
double posA = dist(_line[i - 1], p) + distA;
|
||||
double posB = dist(_line[j - 1], p) + distB;
|
||||
|
||||
if (fabs(posA - posB) < maxl) {
|
||||
_line[i] = p;
|
||||
_line.erase(_line.begin() + i + 1, _line.begin() + j);
|
||||
}
|
||||
}
|
||||
|
||||
distB += dist(_line[j - 1], _line[j]);
|
||||
}
|
||||
distA += dist(_line[i - 1], _line[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void PolyLine<T>::applyChaikinSmooth(size_t depth) {
|
||||
for (size_t i = 0; i < depth; i++) {
|
||||
Line<T> smooth;
|
||||
|
||||
smooth.push_back(_line.front());
|
||||
|
||||
for (size_t i = 1; i < _line.size(); i++) {
|
||||
Point<T> pA = _line[i - 1];
|
||||
Point<T> pB = _line[i];
|
||||
|
||||
smooth.push_back(
|
||||
Point<T>(0.75 * pA.template get<0>() + 0.25 * pB.template get<0>(),
|
||||
0.75 * pA.template get<1>() + 0.25 * pB.template get<1>()));
|
||||
smooth.push_back(
|
||||
Point<T>(0.25 * pA.template get<0>() + 0.75 * pB.template get<0>(),
|
||||
0.25 * pA.template get<1>() + 0.75 * pB.template get<1>()));
|
||||
}
|
||||
|
||||
smooth.push_back(_line.back());
|
||||
_line = smooth;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
const Point<T>& PolyLine<T>::front() const {
|
||||
return _line.front();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
const Point<T>& PolyLine<T>::back() const {
|
||||
return _line.back();
|
||||
}
|
38
src/util/geo/output/GeoGraphJsonOutput.h
Normal file
38
src/util/geo/output/GeoGraphJsonOutput.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_
|
||||
#define UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include "util/String.h"
|
||||
#include "util/geo/output/GeoJsonOutput.h"
|
||||
#include "util/graph/Graph.h"
|
||||
|
||||
using util::toString;
|
||||
using util::graph::Graph;
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
namespace output {
|
||||
|
||||
class GeoGraphJsonOutput {
|
||||
public:
|
||||
inline GeoGraphJsonOutput(){};
|
||||
template <typename N, typename E>
|
||||
void print(const Graph<N, E>& outG, std::ostream& str);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
Line<T> createLine(const util::geo::Point<T>& a,
|
||||
const util::geo::Point<T>& b);
|
||||
};
|
||||
|
||||
#include "util/geo/output/GeoGraphJsonOutput.tpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GEO_OUTPUT_GEOGRAPHJSONOUTPUT_H_
|
62
src/util/geo/output/GeoGraphJsonOutput.tpp
Normal file
62
src/util/geo/output/GeoGraphJsonOutput.tpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
using util::geo::output::Attrs;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
Line<T> GeoGraphJsonOutput::createLine(const util::geo::Point<T>& a,
|
||||
const util::geo::Point<T>& b) {
|
||||
Line<T> ret;
|
||||
ret.push_back(a);
|
||||
ret.push_back(b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void GeoGraphJsonOutput::print(const util::graph::Graph<N, E>& outG,
|
||||
std::ostream& str) {
|
||||
GeoJsonOutput _out(str);
|
||||
|
||||
// first pass, nodes
|
||||
for (util::graph::Node<N, E>* n : outG.getNds()) {
|
||||
if (!n->pl().getGeom()) continue;
|
||||
|
||||
Attrs props = {{"id", toString(n)},
|
||||
{"deg", toString(n->getInDeg() + n->getOutDeg())},
|
||||
{"deg_out", toString(n->getOutDeg())},
|
||||
{"deg_in", toString(n->getInDeg())}};
|
||||
n->pl().getAttrs(&props);
|
||||
|
||||
_out.print(*n->pl().getGeom(), props);
|
||||
}
|
||||
|
||||
// second pass, edges
|
||||
for (graph::Node<N, E>* n : outG.getNds()) {
|
||||
for (graph::Edge<N, E>* e : n->getAdjListOut()) {
|
||||
// to avoid double output for undirected graphs
|
||||
if (e->getFrom() != n) continue;
|
||||
Attrs props{{"from", toString(e->getFrom())},
|
||||
{"to", toString(e->getTo())},
|
||||
{"id", toString(e)}};
|
||||
|
||||
e->pl().getAttrs(&props);
|
||||
|
||||
if (!e->pl().getGeom() || !e->pl().getGeom()->size()) {
|
||||
if (e->getFrom()->pl().getGeom()) {
|
||||
auto a = *e->getFrom()->pl().getGeom();
|
||||
if (e->getTo()->pl().getGeom()) {
|
||||
auto b = *e->getTo()->pl().getGeom();
|
||||
_out.print(createLine(a, b), props);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_out.print(*e->pl().getGeom(), props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_out.flush();
|
||||
}
|
25
src/util/geo/output/GeoJsonOutput.cpp
Normal file
25
src/util/geo/output/GeoJsonOutput.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
//
|
||||
#include "util/geo/output/GeoJsonOutput.h"
|
||||
|
||||
using namespace util;
|
||||
using namespace geo;
|
||||
using namespace output;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
GeoJsonOutput::GeoJsonOutput(std::ostream& str) : _wr(&str, 10, true) {
|
||||
_wr.obj();
|
||||
_wr.keyVal("type", "FeatureCollection");
|
||||
_wr.key("features");
|
||||
_wr.arr();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
GeoJsonOutput::~GeoJsonOutput() {
|
||||
flush();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void GeoJsonOutput::flush() { _wr.closeAll(); }
|
41
src/util/geo/output/GeoJsonOutput.h
Normal file
41
src/util/geo/output/GeoJsonOutput.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_
|
||||
#define UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "util/String.h"
|
||||
#include "util/geo/Geo.h"
|
||||
#include "util/json/JsonWriter.h"
|
||||
|
||||
namespace util {
|
||||
namespace geo {
|
||||
namespace output {
|
||||
|
||||
typedef std::map<std::string, std::string> Attrs;
|
||||
|
||||
class GeoJsonOutput {
|
||||
public:
|
||||
GeoJsonOutput(std::ostream& str);
|
||||
~GeoJsonOutput();
|
||||
template <typename T>
|
||||
void print(const Point<T>& p, Attrs attrs);
|
||||
template <typename T>
|
||||
void print(const Line<T>& l, Attrs attrs);
|
||||
void flush();
|
||||
|
||||
private:
|
||||
json::JsonWriter _wr;
|
||||
};
|
||||
|
||||
#include "util/geo/output/GeoJsonOutput.tpp"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GEO_OUTPUT_GEOJSONOUTPUT_H_
|
48
src/util/geo/output/GeoJsonOutput.tpp
Normal file
48
src/util/geo/output/GeoJsonOutput.tpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::print(const Point<T>& p, Attrs attrs) {
|
||||
_wr.obj();
|
||||
_wr.keyVal("type", "Feature");
|
||||
|
||||
_wr.key("geometry");
|
||||
_wr.obj();
|
||||
_wr.keyVal("type", "Point");
|
||||
_wr.key("coordinates");
|
||||
_wr.arr();
|
||||
_wr.val(p.template get<0>());
|
||||
_wr.val(p.template get<1>());
|
||||
_wr.close();
|
||||
_wr.close();
|
||||
_wr.key("properties");
|
||||
_wr.obj(attrs);
|
||||
_wr.close();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename T>
|
||||
void GeoJsonOutput::print(const Line<T>& line, Attrs attrs) {
|
||||
if (!line.size()) return;
|
||||
_wr.obj();
|
||||
_wr.keyVal("type", "Feature");
|
||||
|
||||
_wr.key("geometry");
|
||||
_wr.obj();
|
||||
_wr.keyVal("type", "LineString");
|
||||
_wr.key("coordinates");
|
||||
_wr.arr();
|
||||
for (auto p : line) {
|
||||
_wr.arr();
|
||||
_wr.val(p.template get<0>());
|
||||
_wr.val(p.template get<1>());
|
||||
_wr.close();
|
||||
}
|
||||
_wr.close();
|
||||
_wr.close();
|
||||
_wr.key("properties");
|
||||
_wr.obj(attrs);
|
||||
_wr.close();
|
||||
}
|
7
src/util/graph/Dijkstra.cpp
Normal file
7
src/util/graph/Dijkstra.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "util/graph/Dijkstra.h"
|
||||
|
||||
size_t util::graph::Dijkstra::ITERS = 0;
|
113
src/util/graph/Dijkstra.h
Normal file
113
src/util/graph/Dijkstra.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_DIJKSTRA_H_
|
||||
#define UTIL_GRAPH_DIJKSTRA_H_
|
||||
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Node.h"
|
||||
#include "util/graph/ShortestPath.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
using util::graph::Edge;
|
||||
|
||||
// dijkstras algorithm for util graph
|
||||
class Dijkstra : public ShortestPath<Dijkstra> {
|
||||
public:
|
||||
template <typename N, typename E, typename C>
|
||||
struct RouteNode {
|
||||
RouteNode() : n(0), parent(0), d(), h(), e(0) {}
|
||||
RouteNode(Node<N, E>* n) : n(n), parent(0), d(), h(), e(0) {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, Edge<N, E>* e)
|
||||
: n(n), parent(parent), d(d), h(), e(e) {}
|
||||
RouteNode(Node<N, E>* n, Node<N, E>* parent, C d, C h, Edge<N, E>* e)
|
||||
: n(n), parent(parent), d(d), h(h), e(e) {}
|
||||
|
||||
Node<N, E>* n;
|
||||
Node<N, E>* parent;
|
||||
|
||||
C d;
|
||||
C h;
|
||||
|
||||
Edge<N, E>* e;
|
||||
|
||||
bool operator<(const RouteNode<N, E, C>& p) const {
|
||||
return h > p.h || (h == p.h && d > p.d);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using Settled = std::unordered_map<Node<N, E>*, RouteNode<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using PQ = std::priority_queue<RouteNode<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc : public ShortestPath::CostFunc<N, E, C> {
|
||||
C operator()(const Edge<N, E>* from, const Node<N, E>* n,
|
||||
const Edge<N, E>* to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(n);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc : public ShortestPath::HeurFunc<N, E, C> {
|
||||
C operator()(const Edge<N, E>* from,
|
||||
const std::set<Edge<N, E>*>& to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc, const ShortestPath::HeurFunc<N, E, C>&,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNode);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Node<N, E>*> from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc, PQ<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void buildPath(Node<N, E>* curN, Settled<N, E, C>& settled,
|
||||
NList<N, E>* resNodes, EList<N, E>* resEdges);
|
||||
|
||||
static size_t ITERS;
|
||||
};
|
||||
|
||||
#include "util/graph/Dijkstra.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GRAPH_DIJKSTRA_H_
|
175
src/util/graph/Dijkstra.tpp
Normal file
175
src/util/graph/Dijkstra.tpp
Normal file
|
@ -0,0 +1,175 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C Dijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (from->getOutDeg() == 0) return costFunc.inf();
|
||||
|
||||
Settled<N, E, C> settled;
|
||||
PQ<N, E, C> pq;
|
||||
bool found = false;
|
||||
|
||||
pq.emplace(from);
|
||||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().n) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.n] = cur;
|
||||
|
||||
if (to.find(cur.n) != to.end()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
relax(cur, to, costFunc, heurFunc, pq);
|
||||
}
|
||||
|
||||
if (!found) return costFunc.inf();
|
||||
|
||||
buildPath(cur.n, settled, resNodes, resEdges);
|
||||
|
||||
return cur.d;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C Dijkstra::shortestPathImpl(const std::set<Node<N, E>*> from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
Settled<N, E, C> settled;
|
||||
PQ<N, E, C> pq;
|
||||
bool found = false;
|
||||
|
||||
// put all nodes in from onto PQ
|
||||
for (auto n : from) pq.emplace(n);
|
||||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().n) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.n] = cur;
|
||||
|
||||
if (to.find(cur.n) != to.end()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
relax(cur, to, costFunc, heurFunc, pq);
|
||||
}
|
||||
|
||||
if (!found) return costFunc.inf();
|
||||
|
||||
buildPath(cur.n, settled, resNodes, resEdges);
|
||||
|
||||
return cur.d;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Node<N, E>*, C> Dijkstra::shortestPathImpl(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
std::unordered_map<Node<N, E>*, C> costs;
|
||||
if (to.size() == 0) return costs;
|
||||
// init costs with inf
|
||||
for (auto n : to) costs[n] = costFunc.inf();
|
||||
|
||||
if (from->getOutDeg() == 0) return costs;
|
||||
|
||||
Settled<N, E, C> settled;
|
||||
PQ<N, E, C> pq;
|
||||
|
||||
size_t found = 0;
|
||||
|
||||
pq.emplace(from);
|
||||
RouteNode<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
Dijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().n) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.n] = cur;
|
||||
|
||||
if (to.find(cur.n) != to.end()) {
|
||||
found++;
|
||||
}
|
||||
|
||||
if (found == to.size()) break;
|
||||
|
||||
relax(cur, to, costFunc, heurFunc, pq);
|
||||
}
|
||||
|
||||
for (auto nto : to) {
|
||||
if (!settled.count(nto)) continue;
|
||||
Node<N, E>* curN = nto;
|
||||
costs[nto] = settled[curN].d;
|
||||
|
||||
buildPath(nto, settled, resNodes[nto], resEdges[nto]);
|
||||
}
|
||||
|
||||
return costs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void Dijkstra::relax(RouteNode<N, E, C>& cur, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc, PQ<N, E, C>& pq) {
|
||||
for (auto edge : cur.n->getAdjListOut()) {
|
||||
C newC = costFunc(cur.n, edge, edge->getOtherNd(cur.n));
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& newH = newC + heurFunc(edge->getOtherNd(cur.n), to);
|
||||
|
||||
pq.emplace(edge->getOtherNd(cur.n), cur.n, newC, newH, &(*edge));
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void Dijkstra::buildPath(Node<N, E>* curN,
|
||||
Settled<N, E, C>& settled, NList<N, E>* resNodes,
|
||||
EList<N, E>* resEdges) {
|
||||
while (true) {
|
||||
const RouteNode<N, E, C>& curNode = settled[curN];
|
||||
if (resNodes) resNodes->push_back(curNode.n);
|
||||
if (!curNode.parent) break;
|
||||
if (resEdges) resEdges->push_back(curNode.e);
|
||||
curN = curNode.parent;
|
||||
}
|
||||
}
|
41
src/util/graph/DirGraph.h
Normal file
41
src/util/graph/DirGraph.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_DIRGRAPH_H_
|
||||
#define UTIL_GRAPH_DIRGRAPH_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/DirNode.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
template <typename N, typename E>
|
||||
using UndirEdge = Edge<N, E>;
|
||||
|
||||
template <typename N, typename E>
|
||||
class DirGraph : public Graph<N, E> {
|
||||
public:
|
||||
explicit DirGraph();
|
||||
|
||||
using Graph<N, E>::addEdg;
|
||||
|
||||
Node<N, E>* addNd();
|
||||
Node<N, E>* addNd(DirNode<N, E>* n);
|
||||
Node<N, E>* addNd(const N& pl);
|
||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, const E& p);
|
||||
|
||||
Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b);
|
||||
|
||||
};
|
||||
|
||||
#include "util/graph/DirGraph.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GRAPH_DIRGRAPH_H_
|
59
src/util/graph/DirGraph.tpp
Normal file
59
src/util/graph/DirGraph.tpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
DirGraph<N, E>::DirGraph() {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* DirGraph<N, E>::addNd(const N& pl) {
|
||||
return addNd(new DirNode<N, E>(pl));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* DirGraph<N, E>::addNd() {
|
||||
return addNd(new DirNode<N, E>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* DirGraph<N, E>::addNd(DirNode<N, E>* n) {
|
||||
auto ins = Graph<N, E>::getNds()->insert(n);
|
||||
return *ins.first;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Edge<N, E>* DirGraph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to,
|
||||
const E& p) {
|
||||
Edge<N, E>* e = Graph<N, E>::getEdg(from, to);
|
||||
if (!e) {
|
||||
e = new Edge<N, E>(from, to, p);
|
||||
from->addEdge(e);
|
||||
to->addEdge(e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* DirGraph<N, E>::mergeNds(Node<N, E>* a, Node<N, E>* b) {
|
||||
for (auto e : a->getAdjListOut()) {
|
||||
if (e->getTo() != b) {
|
||||
addEdg(b, e->getTo(), e->pl());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto e : a->getAdjListIn()) {
|
||||
if (e->getFrom() != b) {
|
||||
addEdg(e->getFrom(), b, e->pl());
|
||||
}
|
||||
}
|
||||
|
||||
DirGraph<N, E>::delNd(a);
|
||||
|
||||
return b;
|
||||
}
|
57
src/util/graph/DirNode.h
Normal file
57
src/util/graph/DirNode.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_DIRNODE_H_
|
||||
#define UTIL_GRAPH_DIRNODE_H_
|
||||
|
||||
#include <vector>
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
// forward declaration of Edge
|
||||
|
||||
template <typename N, typename E>
|
||||
class DirNode : public Node<N, E> {
|
||||
public:
|
||||
DirNode();
|
||||
DirNode(const N& pl);
|
||||
~DirNode();
|
||||
|
||||
const std::vector<Edge<N, E>*>& getAdjList() const;
|
||||
const std::vector<Edge<N, E>*>& getAdjListIn() const;
|
||||
const std::vector<Edge<N, E>*>& getAdjListOut() const;
|
||||
|
||||
size_t getDeg() const;
|
||||
size_t getInDeg() const;
|
||||
size_t getOutDeg() const;
|
||||
|
||||
bool hasEdgeIn(const Edge<N, E>* e) const;
|
||||
bool hasEdgeOut(const Edge<N, E>* e) const;
|
||||
bool hasEdge(const Edge<N, E>* e) const;
|
||||
|
||||
// add edge to this node's adjacency lists
|
||||
void addEdge(Edge<N, E>* e);
|
||||
|
||||
// remove edge from this node's adjacency lists
|
||||
void removeEdge(Edge<N, E>* e);
|
||||
|
||||
N& pl();
|
||||
const N& pl() const;
|
||||
|
||||
private:
|
||||
std::vector<Edge<N, E>*> _adjListIn;
|
||||
std::vector<Edge<N, E>*> _adjListOut;
|
||||
N _pl;
|
||||
|
||||
bool adjInContains(const Edge<N, E>* e) const;
|
||||
bool adjOutContains(const Edge<N, E>* e) const;
|
||||
};
|
||||
|
||||
#include "util/graph/DirNode.tpp"
|
||||
|
||||
}}
|
||||
|
||||
#endif // UTIL_GRAPH_DIRNODE_H_
|
153
src/util/graph/DirNode.tpp
Normal file
153
src/util/graph/DirNode.tpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
DirNode<N, E>::DirNode() : _pl() {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
DirNode<N, E>::DirNode(const N& pl) : _pl(pl) {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
DirNode<N, E>::~DirNode() {
|
||||
// delete self edges
|
||||
for (auto e = _adjListOut.begin(); e != _adjListOut.end();) {
|
||||
Edge<N, E>* eP = *e;
|
||||
if (eP->getTo() == this) {
|
||||
_adjListIn.erase(std::find(_adjListIn.begin(), _adjListIn.end(), eP));
|
||||
e = _adjListOut.erase(e);
|
||||
delete eP;
|
||||
} else {
|
||||
e++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto e = _adjListOut.begin(); e != _adjListOut.end(); e++) {
|
||||
Edge<N, E>* eP = *e;
|
||||
|
||||
if (eP->getTo() != this) {
|
||||
eP->getTo()->removeEdge(eP);
|
||||
delete eP;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto e = _adjListIn.begin(); e != _adjListIn.end(); e++) {
|
||||
Edge<N, E>* eP = *e;
|
||||
|
||||
if (eP->getFrom() != this) {
|
||||
eP->getFrom()->removeEdge(eP);
|
||||
delete eP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void DirNode<N, E>::addEdge(Edge<N, E>* e) {
|
||||
if (e->getFrom() == this && !adjOutContains(e)) {
|
||||
_adjListOut.reserve(_adjListOut.size() + 1);
|
||||
_adjListOut.push_back(e);
|
||||
}
|
||||
if (e->getTo() == this && !adjInContains(e)) {
|
||||
_adjListIn.reserve(_adjListIn.size() + 1);
|
||||
_adjListIn.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void DirNode<N, E>::removeEdge(Edge<N, E>* e) {
|
||||
if (e->getFrom() == this) {
|
||||
auto p = std::find(_adjListOut.begin(), _adjListOut.end(), e);
|
||||
if (p != _adjListOut.end()) _adjListOut.erase(p);
|
||||
}
|
||||
if (e->getTo() == this) {
|
||||
auto p = std::find(_adjListIn.begin(), _adjListIn.end(), e);
|
||||
if (p != _adjListIn.end()) _adjListIn.erase(p);
|
||||
}
|
||||
}
|
||||
//
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::hasEdgeIn(const Edge<N, E>* e) const {
|
||||
return e->getTo() == this;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::hasEdgeOut(const Edge<N, E>* e) const {
|
||||
return e->getFrom() == this;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::hasEdge(const Edge<N, E>* e) const {
|
||||
return hasEdgeOut(e) || hasEdgeIn(e);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const std::vector<Edge<N, E>*>& DirNode<N, E>::getAdjList() const {
|
||||
return _adjListOut;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const std::vector<Edge<N, E>*>& DirNode<N, E>::getAdjListOut() const {
|
||||
return _adjListOut;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const std::vector<Edge<N, E>*>& DirNode<N, E>::getAdjListIn() const {
|
||||
return _adjListIn;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
size_t DirNode<N, E>::getDeg() const {
|
||||
return _adjListOut.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
size_t DirNode<N, E>::getInDeg() const {
|
||||
return _adjListIn.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
size_t DirNode<N, E>::getOutDeg() const {
|
||||
return _adjListOut.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
N& DirNode<N, E>::pl() {
|
||||
return _pl;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const N& DirNode<N, E>::pl() const {
|
||||
return _pl;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::adjInContains(const Edge<N, E>* e) const {
|
||||
for (size_t i = 0; i < _adjListIn.size(); i++)
|
||||
if (_adjListIn[i] == e) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool DirNode<N, E>::adjOutContains(const Edge<N, E>* e) const {
|
||||
for (size_t i = 0; i < _adjListOut.size(); i++)
|
||||
if (_adjListOut[i] == e) return true;
|
||||
return false;
|
||||
}
|
7
src/util/graph/EDijkstra.cpp
Normal file
7
src/util/graph/EDijkstra.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include "util/graph/EDijkstra.h"
|
||||
|
||||
size_t util::graph::EDijkstra::ITERS = 0;
|
139
src/util/graph/EDijkstra.h
Normal file
139
src/util/graph/EDijkstra.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_EDIJKSTRA_H_
|
||||
#define UTIL_GRAPH_EDIJKSTRA_H_
|
||||
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Node.h"
|
||||
#include "util/graph/ShortestPath.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
using util::graph::Edge;
|
||||
|
||||
// edge-based dijkstra - settles edges instead of nodes
|
||||
class EDijkstra : public ShortestPath<EDijkstra> {
|
||||
public:
|
||||
template <typename N, typename E, typename C>
|
||||
struct RouteEdge {
|
||||
RouteEdge() : e(0), parent(0), d(), h(), n(0) {}
|
||||
RouteEdge(Edge<N, E>* e) : e(e), parent(0), d(), h(), n(0) {}
|
||||
RouteEdge(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d)
|
||||
: e(e), parent(parent), d(d), h(), n(n) {}
|
||||
RouteEdge(Edge<N, E>* e, Edge<N, E>* parent, Node<N, E>* n, C d, C h)
|
||||
: e(e), parent(parent), d(d), h(h), n(n) {}
|
||||
|
||||
Edge<N, E>* e;
|
||||
Edge<N, E>* parent;
|
||||
|
||||
C d;
|
||||
C h;
|
||||
|
||||
Node<N, E>* n;
|
||||
|
||||
bool operator<(const RouteEdge<N, E, C>& p) const {
|
||||
return h > p.h || (h == p.h && d > p.d);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc : public ShortestPath::CostFunc<N, E, C> {
|
||||
C operator()(const Node<N, E>* from, const Edge<N, E>* e,
|
||||
const Node<N, E>* to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(e);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc : public ShortestPath::HeurFunc<N, E, C> {
|
||||
C operator()(const Node<N, E>* from,
|
||||
const std::set<Node<N, E>*>& to) const {
|
||||
UNUSED(from);
|
||||
UNUSED(to);
|
||||
return C();
|
||||
};
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using Settled = std::unordered_map<Edge<N, E>*, RouteEdge<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
using PQ = std::priority_queue<RouteEdge<N, E, C> >;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Edge<N, E>*> from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPathImpl(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
|
||||
const std::set<Edge<N, E>*>& from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc, bool rev);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathImpl(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void buildPath(Edge<N, E>* curE, const Settled<N, E, C>& settled,
|
||||
NList<N, E>* resNodes, EList<N, E>* resEdges);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static inline void relax(RouteEdge<N, E, C>& cur,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static void relaxInv(RouteEdge<N, E, C>& cur,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
PQ<N, E, C>& pq);
|
||||
|
||||
static size_t ITERS;
|
||||
};
|
||||
|
||||
#include "util/graph/EDijkstra.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GRAPH_DIJKSTRA_H_
|
258
src/util/graph/EDijkstra.tpp
Normal file
258
src/util/graph/EDijkstra.tpp
Normal file
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C EDijkstra::shortestPathImpl(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
std::set<Edge<N, E>*> frEs;
|
||||
std::set<Edge<N, E>*> toEs;
|
||||
|
||||
frEs.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
|
||||
|
||||
for (auto n : to) {
|
||||
toEs.insert(n->getAdjListIn().begin(), n->getAdjListIn().end());
|
||||
}
|
||||
|
||||
C cost = shortestPathImpl(frEs, toEs, costFunc, heurFunc, resEdges, resNodes);
|
||||
|
||||
// the beginning node is not included in our edge based dijkstra
|
||||
if (resNodes) resNodes->push_back(from);
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C EDijkstra::shortestPathImpl(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
std::set<Edge<N, E>*> frEs;
|
||||
std::set<Edge<N, E>*> toEs;
|
||||
|
||||
frEs.insert(from);
|
||||
|
||||
for (auto n : to) {
|
||||
toEs.insert(n->getAdjListIn().begin(), n->getAdjListIn().end());
|
||||
}
|
||||
|
||||
C cost = shortestPathImpl(frEs, toEs, costFunc, heurFunc, resEdges, resNodes);
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
C EDijkstra::shortestPathImpl(const std::set<Edge<N, E>*> from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (from.size() == 0 || to.size() == 0) return costFunc.inf();
|
||||
|
||||
Settled<N, E, C> settled;
|
||||
PQ<N, E, C> pq;
|
||||
bool found = false;
|
||||
|
||||
// at the beginning, put all edges on the priority queue,
|
||||
// init them with their own cost
|
||||
for (auto e : from) {
|
||||
C c = costFunc(0, 0, e);
|
||||
C h = heurFunc(e, to);
|
||||
pq.emplace(e, (Edge<N, E>*)0, (Node<N, E>*)0, c, c + h);
|
||||
}
|
||||
|
||||
RouteEdge<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().e) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
||||
if (to.find(cur.e) != to.end()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
relax(cur, to, costFunc, heurFunc, pq);
|
||||
}
|
||||
|
||||
if (!found) return costFunc.inf();
|
||||
|
||||
buildPath(cur.e, settled, resNodes, resEdges);
|
||||
|
||||
return cur.d;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
||||
const std::set<Edge<N, E>*>& from, const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
bool rev) {
|
||||
std::unordered_map<Edge<N, E>*, C> costs;
|
||||
|
||||
Settled<N, E, C> settled;
|
||||
PQ<N, E, C> pq;
|
||||
|
||||
std::set<Edge<N, E>*> to;
|
||||
|
||||
for (auto e : from) {
|
||||
pq.emplace(e, (Edge<N, E>*)0, (Node<N, E>*)0, costFunc(0, 0, e), C());
|
||||
}
|
||||
|
||||
RouteEdge<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().e) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
||||
costs[cur.e] = cur.d;
|
||||
buildPath(cur.e, settled, (NList<N, E>*)0, (EList<N, E>*)0);
|
||||
|
||||
if (rev)
|
||||
relaxInv(cur, costFunc, pq);
|
||||
else
|
||||
relax(cur, to, costFunc, ZeroHeurFunc<N, E, C>(), pq);
|
||||
}
|
||||
|
||||
return costs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
std::unordered_map<Edge<N, E>*, C> EDijkstra::shortestPathImpl(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
std::unordered_map<Edge<N, E>*, C> costs;
|
||||
if (to.size() == 0) return costs;
|
||||
|
||||
// init costs with inf
|
||||
for (auto e : to) costs[e] = costFunc.inf();
|
||||
|
||||
Settled<N, E, C> settled;
|
||||
PQ<N, E, C> pq;
|
||||
|
||||
size_t found = 0;
|
||||
|
||||
C c = costFunc(0, 0, from);
|
||||
C h = heurFunc(from, to);
|
||||
pq.emplace(from, (Edge<N, E>*)0, (Node<N, E>*)0, c, c + h);
|
||||
|
||||
RouteEdge<N, E, C> cur;
|
||||
|
||||
while (!pq.empty()) {
|
||||
EDijkstra::ITERS++;
|
||||
|
||||
if (settled.find(pq.top().e) != settled.end()) {
|
||||
pq.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = pq.top();
|
||||
pq.pop();
|
||||
|
||||
settled[cur.e] = cur;
|
||||
|
||||
if (to.find(cur.e) != to.end()) {
|
||||
found++;
|
||||
costs[cur.e] = cur.d;
|
||||
buildPath(cur.e, settled, resNodes[cur.e], resEdges[cur.e]);
|
||||
}
|
||||
|
||||
if (found == to.size()) return costs;
|
||||
|
||||
relax(cur, to, costFunc, heurFunc, pq);
|
||||
}
|
||||
|
||||
return costs;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::relaxInv(RouteEdge<N, E, C>& cur,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
|
||||
// handling undirected graph makes no sense here
|
||||
|
||||
for (const auto edge : cur.e->getFrom()->getAdjListIn()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(edge, cur.e->getFrom(), cur.e);
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
pq.emplace(edge, cur.e, cur.e->getFrom(), newC, C());
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::relax(RouteEdge<N, E, C>& cur, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const ShortestPath::HeurFunc<N, E, C>& heurFunc,
|
||||
PQ<N, E, C>& pq) {
|
||||
if (cur.e->getFrom()->hasEdgeIn(cur.e)) {
|
||||
// for undirected graphs
|
||||
for (const auto edge : cur.e->getFrom()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getFrom(), edge);
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
const C& newH = newC + h;
|
||||
|
||||
pq.emplace(edge, cur.e, cur.e->getFrom(), newC, newH);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto edge : cur.e->getTo()->getAdjListOut()) {
|
||||
if (edge == cur.e) continue;
|
||||
C newC = costFunc(cur.e, cur.e->getTo(), edge);
|
||||
newC = cur.d + newC;
|
||||
if (costFunc.inf() <= newC) continue;
|
||||
|
||||
const C& h = heurFunc(edge, to);
|
||||
const C& newH = newC + h;
|
||||
|
||||
pq.emplace(edge, cur.e, cur.e->getTo(), newC, newH);
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E, typename C>
|
||||
void EDijkstra::buildPath(Edge<N, E>* curE, const Settled<N, E, C>& settled,
|
||||
NList<N, E>* resNodes, EList<N, E>* resEdges) {
|
||||
const RouteEdge<N, E, C>* curEdge = &settled.find(curE)->second;
|
||||
if (resNodes) resNodes->push_back(curEdge->e->getOtherNd(curEdge->n));
|
||||
while (true) {
|
||||
if (resNodes && curEdge->n) resNodes->push_back(curEdge->n);
|
||||
if (resEdges) resEdges->push_back(curEdge->e);
|
||||
if (!curEdge->parent) break;
|
||||
curEdge = &settled.find(curEdge->parent)->second;
|
||||
}
|
||||
}
|
41
src/util/graph/Edge.h
Normal file
41
src/util/graph/Edge.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_EDGE_H_
|
||||
#define UTIL_GRAPH_EDGE_H_
|
||||
|
||||
#include <vector>
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
template <typename N, typename E>
|
||||
class Edge {
|
||||
public:
|
||||
Edge(Node<N, E>* from, Node<N, E>* to, const E& pl);
|
||||
|
||||
Node<N, E>* getFrom() const;
|
||||
Node<N, E>* getTo() const;
|
||||
|
||||
Node<N, E>* getOtherNd(const Node<N, E>* notNode) const;
|
||||
|
||||
void setFrom(Node<N, E>* from);
|
||||
void setTo(Node<N, E>* to);
|
||||
|
||||
E& pl();
|
||||
const E& pl() const;
|
||||
|
||||
private:
|
||||
Node<N, E>* _from;
|
||||
Node<N, E>* _to;
|
||||
E _pl;
|
||||
};
|
||||
|
||||
#include "util/graph/Edge.tpp"
|
||||
|
||||
}}
|
||||
|
||||
#endif // UTIL_GRAPH_EDGE_H_
|
||||
|
41
src/util/graph/Edge.tpp
Normal file
41
src/util/graph/Edge.tpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Edge<N, E>::Edge(Node<N, E>* from, Node<N, E>* to, const E& pl)
|
||||
: _from(from), _to(to), _pl(pl) {
|
||||
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* Edge<N, E>::getFrom() const {
|
||||
return _from;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* Edge<N, E>::getTo() const {
|
||||
return _to;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* Edge<N, E>::getOtherNd(const Node<N, E>* notNode) const {
|
||||
if (_to == notNode) return _from;
|
||||
return _to;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
E& Edge<N, E>::pl() {
|
||||
return _pl;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const E& Edge<N, E>::pl() const {
|
||||
return _pl;
|
||||
}
|
47
src/util/graph/Graph.h
Normal file
47
src/util/graph/Graph.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_GRAPH_H_
|
||||
#define UTIL_GRAPH_GRAPH_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
template <typename N, typename E>
|
||||
class Graph {
|
||||
public:
|
||||
~Graph();
|
||||
virtual Node<N, E>* addNd() = 0;
|
||||
virtual Node<N, E>* addNd(const N& pl) = 0;
|
||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to);
|
||||
virtual Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, const E& p) = 0;
|
||||
Edge<N, E>* getEdg(Node<N, E>* from, Node<N, E>* to);
|
||||
const Edge<N, E>* getEdg(Node<N, E>* from, Node<N, E>* to) const;
|
||||
|
||||
virtual Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b) = 0;
|
||||
|
||||
const std::set<Node<N, E>*>& getNds() const;
|
||||
std::set<Node<N, E>*>* getNds();
|
||||
|
||||
typename std::set<Node<N, E>*>::iterator delNd(Node<N, E>* n);
|
||||
typename std::set<Node<N, E>*>::iterator delNd(
|
||||
typename std::set<Node<N, E>*>::iterator i);
|
||||
void delEdg(Node<N, E>* from, Node<N, E>* to);
|
||||
|
||||
private:
|
||||
std::set<Node<N, E>*> _nodes;
|
||||
};
|
||||
|
||||
#include "util/graph/Graph.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GRAPH_GRAPH_H_
|
76
src/util/graph/Graph.tpp
Normal file
76
src/util/graph/Graph.tpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Graph<N, E>::~Graph() {
|
||||
for (auto n : _nodes) delete n;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Edge<N, E>* Graph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to) {
|
||||
return addEdg(from, to, E());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const std::set<Node<N, E>*>& Graph<N, E>::getNds() const {
|
||||
return _nodes;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
std::set<Node<N, E>*>* Graph<N, E>::getNds() {
|
||||
return &_nodes;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
typename std::set<Node<N, E>*>::iterator Graph<N, E>::delNd(
|
||||
Node<N, E>* n) {
|
||||
return delNd(_nodes.find(n));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
typename std::set<Node<N, E>*>::iterator Graph<N, E>::delNd(
|
||||
typename std::set<Node<N, E>*>::iterator i) {
|
||||
delete *i;
|
||||
return _nodes.erase(i);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void Graph<N, E>::delEdg(Node<N, E>* from, Node<N, E>* to) {
|
||||
Edge<N, E>* toDel = getEdg(from, to);
|
||||
if (!toDel) return;
|
||||
|
||||
from->removeEdge(toDel);
|
||||
to->removeEdge(toDel);
|
||||
|
||||
assert(!getEdg(from, to));
|
||||
|
||||
delete toDel;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Edge<N, E>* Graph<N, E>::getEdg(Node<N, E>* from, Node<N, E>* to) {
|
||||
for (auto e : from->getAdjList()) {
|
||||
if (e->getOtherNd(from) == to) return e;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const Edge<N, E>* Graph<N, E>::getEdg(Node<N, E>* from, Node<N, E>* to) const {
|
||||
for (auto e : from->getAdjList()) {
|
||||
if (e->getOtherNd(from) == to) return e;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
44
src/util/graph/Node.h
Normal file
44
src/util/graph/Node.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_NODE_H_
|
||||
#define UTIL_GRAPH_NODE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
// forward declaration of Edge
|
||||
template <typename N, typename E>
|
||||
class Edge;
|
||||
|
||||
template <typename N, typename E>
|
||||
class Node {
|
||||
public:
|
||||
virtual const std::vector<Edge<N, E>*>& getAdjList() const = 0;
|
||||
virtual const std::vector<Edge<N, E>*>& getAdjListOut() const = 0;
|
||||
virtual const std::vector<Edge<N, E>*>& getAdjListIn() const = 0;
|
||||
|
||||
virtual size_t getDeg() const = 0;
|
||||
virtual size_t getInDeg() const = 0;
|
||||
virtual size_t getOutDeg() const = 0;
|
||||
|
||||
virtual bool hasEdgeIn(const Edge<N, E>* e) const = 0;
|
||||
virtual bool hasEdgeOut(const Edge<N, E>* e) const = 0;
|
||||
virtual bool hasEdge(const Edge<N, E>* e) const = 0;
|
||||
|
||||
// add edge to this node's adjacency lists
|
||||
virtual void addEdge(Edge<N, E>* e) = 0;
|
||||
virtual void removeEdge(Edge<N, E>* e) = 0;
|
||||
|
||||
virtual ~Node() {};
|
||||
|
||||
virtual N& pl() = 0;
|
||||
virtual const N& pl() const = 0;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // UTIL_GRAPH_NODE_H_
|
401
src/util/graph/ShortestPath.h
Normal file
401
src/util/graph/ShortestPath.h
Normal file
|
@ -0,0 +1,401 @@
|
|||
// Copyright 2017, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_SHORTESTPATH_H_
|
||||
#define UTIL_GRAPH_SHORTESTPATH_H_
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
using util::graph::Graph;
|
||||
using util::graph::Node;
|
||||
using util::graph::Edge;
|
||||
|
||||
// shortest path base class
|
||||
template <class D>
|
||||
class ShortestPath {
|
||||
public:
|
||||
template <typename N, typename E>
|
||||
using EList = std::vector<Edge<N, E>*>;
|
||||
|
||||
template <typename N, typename E>
|
||||
using NList = std::vector<Node<N, E>*>;
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct CostFunc {
|
||||
virtual C operator()(const Node<N, E>* from, const Edge<N, E>* e,
|
||||
const Node<N, E>* to) const = 0;
|
||||
virtual C operator()(const Edge<N, E>* from, const Node<N, E>* n,
|
||||
const Edge<N, E>* to) const = 0;
|
||||
virtual C inf() const = 0;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct HeurFunc {
|
||||
virtual C operator()(const Node<N, E>* a,
|
||||
const std::set<Node<N, E>*>& b) const = 0;
|
||||
virtual C operator()(const Edge<N, E>* a,
|
||||
const std::set<Edge<N, E>*>& b) const = 0;
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
struct ZeroHeurFunc : public HeurFunc<N, E, C> {
|
||||
C operator()(const Node<N, E>* a, const std::set<Node<N, E>*>& b) const {
|
||||
UNUSED(a);
|
||||
UNUSED(b);
|
||||
return C();
|
||||
}
|
||||
C operator()(const Edge<N, E>* a, const std::set<Edge<N, E>*>& b) const {
|
||||
UNUSED(a);
|
||||
UNUSED(b);
|
||||
return C();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Node<N, E>*> from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
|
||||
resEdges, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
|
||||
resEdges, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges) {
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> resEdges) {
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
|
||||
resEdges, dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(),
|
||||
dummyRet, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Node<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc, const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Node<N, E>*, NList<N, E>*> resNodes) {
|
||||
std::unordered_map<Node<N, E>*, EList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, dummyRet,
|
||||
resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (to->getInDeg() == 0) return costFunc.inf();
|
||||
std::set<Node<N, E>*> tos;
|
||||
tos.insert(to);
|
||||
return shortestPath(from, tos, costFunc, resEdges, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc) {
|
||||
if (to->getInDeg() == 0) return costFunc.inf();
|
||||
std::set<Node<N, E>*> tos;
|
||||
tos.insert(to);
|
||||
EList<N, E>* el = 0;
|
||||
NList<N, E>* nl = 0;
|
||||
return shortestPath(from, tos, costFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
NList<N, E>* resNodes) {
|
||||
if (to->getInDeg() == 0) return costFunc.inf();
|
||||
std::set<Node<N, E>*> tos;
|
||||
tos.insert(to);
|
||||
EList<N, E>* el = 0;
|
||||
return shortestPath(from, tos, costFunc, el, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
EList<N, E>* resEdges) {
|
||||
if (to->getInDeg() == 0) return costFunc.inf();
|
||||
std::set<Node<N, E>*> tos;
|
||||
tos.insert(to);
|
||||
NList<N, E>* nl = 0;
|
||||
return shortestPath(from, tos, costFunc, resEdges, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
NList<N, E>* resNodes) {
|
||||
EList<N, E>* el = 0;
|
||||
return shortestPath(from, to, costFunc, el, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
EList<N, E>* resEdges) {
|
||||
NList<N, E>* nl = 0;
|
||||
return shortestPath(from, to, costFunc, resEdges, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges, NList<N, E>* resNodes) {
|
||||
if (to->getInDeg() == 0) return costFunc.inf();
|
||||
std::set<Node<N, E>*> tos;
|
||||
tos.insert(to);
|
||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, resEdges,
|
||||
resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
NList<N, E>* resNodes) {
|
||||
if (to->getInDeg() == 0) return costFunc.inf();
|
||||
std::set<Node<N, E>*> tos;
|
||||
tos.insert(to);
|
||||
EList<N, E>* el = 0;
|
||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, el, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges) {
|
||||
if (to->getInDeg() == 0) return costFunc.inf();
|
||||
std::set<Node<N, E>*> tos;
|
||||
tos.insert(to);
|
||||
NList<N, E>* nl = 0;
|
||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, resEdges, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
NList<N, E>* resNodes) {
|
||||
EList<N, E>* el = 0;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Node<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
EList<N, E>* resEdges) {
|
||||
NList<N, E>* nl = 0;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges,
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> resNodes) {
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
resNodes);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc,
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> resEdges) {
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, resEdges,
|
||||
dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from, const std::set<Edge<N, E>*>& to,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc) {
|
||||
std::unordered_map<Edge<N, E>*, NList<N, E>*> dummyRet;
|
||||
std::unordered_map<Edge<N, E>*, EList<N, E>*> dummyRetE;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, dummyRetE,
|
||||
dummyRet);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc) {
|
||||
NList<N, E>* nl = 0;
|
||||
EList<N, E>* el = 0;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from,
|
||||
Edge<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc ) {
|
||||
NList<N, E>* nl = 0;
|
||||
EList<N, E>* el = 0;
|
||||
std::set<Edge<N, E>*> tos{to};
|
||||
std::set<Edge<N, E>*> froms{from};
|
||||
return D::shortestPathImpl(froms, tos, costFunc, ZeroHeurFunc<N, E, C>(), el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(const std::set<Edge<N, E>*>& from,
|
||||
const std::set<Edge<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc, EList<N, E>* el) {
|
||||
NList<N, E>* nl = 0;
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc, EList<N, E>* el,
|
||||
NList<N, E>* nl) {
|
||||
return D::shortestPathImpl(from, to, costFunc, heurFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, const std::set<Node<N, E>*>& to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
EList<N, E>* el,
|
||||
NList<N, E>* nl) {
|
||||
return D::shortestPathImpl(from, to, costFunc, ZeroHeurFunc<N, E, C>(), el,
|
||||
nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc,
|
||||
const HeurFunc<N, E, C>& heurFunc, EList<N, E>* el,
|
||||
NList<N, E>* nl) {
|
||||
std::set<Node<N, E>*> tos{to};
|
||||
return D::shortestPathImpl(from, tos, costFunc, heurFunc, el, nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static C shortestPath(Edge<N, E>* from, Node<N, E>* to,
|
||||
const CostFunc<N, E, C>& costFunc, EList<N, E>* el,
|
||||
NList<N, E>* nl) {
|
||||
std::set<Node<N, E>*> tos{to};
|
||||
return D::shortestPathImpl(from, tos, costFunc, ZeroHeurFunc<N, E, C>(), el,
|
||||
nl);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Edge<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms { from };
|
||||
return D::shortestPathImpl(froms, costFunc, false);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathRev(
|
||||
Edge<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms { from };
|
||||
return D::shortestPathImpl(froms, costFunc, true);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPath(
|
||||
Node<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms;
|
||||
froms.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
|
||||
return D::shortestPathImpl(froms, costFunc, false);
|
||||
}
|
||||
|
||||
template <typename N, typename E, typename C>
|
||||
static std::unordered_map<Edge<N, E>*, C> shortestPathRev(
|
||||
Node<N, E>* from,
|
||||
const ShortestPath::CostFunc<N, E, C>& costFunc) {
|
||||
std::set<Edge<N, E>*> froms;
|
||||
froms.insert(from->getAdjListOut().begin(), from->getAdjListOut().end());
|
||||
return D::shortestPathImpl(froms, costFunc, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GRAPH_SHORTESTPATH_H_
|
1
src/util/graph/ShortestPath.tpp
Normal file
1
src/util/graph/ShortestPath.tpp
Normal file
|
@ -0,0 +1 @@
|
|||
|
41
src/util/graph/UndirGraph.h
Normal file
41
src/util/graph/UndirGraph.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_UNDIRGRAPH_H_
|
||||
#define UTIL_GRAPH_UNDIRGRAPH_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "util/graph/Graph.h"
|
||||
#include "util/graph/Edge.h"
|
||||
#include "util/graph/UndirNode.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
template <typename N, typename E>
|
||||
using UndirEdge = Edge<N, E>;
|
||||
|
||||
template <typename N, typename E>
|
||||
class UndirGraph : public Graph<N, E> {
|
||||
public:
|
||||
explicit UndirGraph();
|
||||
|
||||
using Graph<N, E>::addEdg;
|
||||
|
||||
Node<N, E>* addNd();
|
||||
Node<N, E>* addNd(UndirNode<N, E>* n);
|
||||
Node<N, E>* addNd(const N& pl);
|
||||
Edge<N, E>* addEdg(Node<N, E>* from, Node<N, E>* to, const E& p);
|
||||
|
||||
Node<N, E>* mergeNds(Node<N, E>* a, Node<N, E>* b);
|
||||
|
||||
};
|
||||
|
||||
#include "util/graph/UndirGraph.tpp"
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UTIL_GRAPH_UNDIRGRAPH_H_
|
59
src/util/graph/UndirGraph.tpp
Normal file
59
src/util/graph/UndirGraph.tpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
UndirGraph<N, E>::UndirGraph() {}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* UndirGraph<N, E>::addNd(const N& pl) {
|
||||
return addNd(new UndirNode<N, E>(pl));
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* UndirGraph<N, E>::addNd() {
|
||||
return addNd(new UndirNode<N, E>());
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* UndirGraph<N, E>::addNd(UndirNode<N, E>* n) {
|
||||
auto ins = Graph<N, E>::getNds()->insert(n);
|
||||
return *ins.first;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Edge<N, E>* UndirGraph<N, E>::addEdg(Node<N, E>* from, Node<N, E>* to,
|
||||
const E& p) {
|
||||
Edge<N, E>* e = Graph<N, E>::getEdg(from, to);
|
||||
if (!e) {
|
||||
e = new Edge<N, E>(from, to, p);
|
||||
from->addEdge(e);
|
||||
to->addEdge(e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
Node<N, E>* UndirGraph<N, E>::mergeNds(Node<N, E>* a, Node<N, E>* b) {
|
||||
for (auto e : a->getAdjListOut()) {
|
||||
if (e->getTo() != b) {
|
||||
addEdg(b, e->getTo(), e->pl());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto e : a->getAdjListIn()) {
|
||||
if (e->getFrom() != b) {
|
||||
addEdg(e->getFrom(), b, e->pl());
|
||||
}
|
||||
}
|
||||
|
||||
UndirGraph<N, E>::delNd(a);
|
||||
|
||||
return b;
|
||||
}
|
53
src/util/graph/UndirNode.h
Normal file
53
src/util/graph/UndirNode.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_GRAPH_UNDIRNODE_H_
|
||||
#define UTIL_GRAPH_UNDIRNODE_H_
|
||||
|
||||
#include <vector>
|
||||
#include "util/graph/Node.h"
|
||||
|
||||
namespace util {
|
||||
namespace graph {
|
||||
|
||||
template <typename N, typename E>
|
||||
class UndirNode : public Node<N, E> {
|
||||
public:
|
||||
UndirNode();
|
||||
UndirNode(const N& pl);
|
||||
~UndirNode();
|
||||
|
||||
const std::vector<Edge<N, E>*>& getAdjList() const;
|
||||
const std::vector<Edge<N, E>*>& getAdjListIn() const;
|
||||
const std::vector<Edge<N, E>*>& getAdjListOut() const;
|
||||
|
||||
size_t getDeg() const;
|
||||
size_t getInDeg() const;
|
||||
size_t getOutDeg() const;
|
||||
|
||||
bool hasEdgeIn(const Edge<N, E>* e) const;
|
||||
bool hasEdgeOut(const Edge<N, E>* e) const;
|
||||
bool hasEdge(const Edge<N, E>* e) const;
|
||||
|
||||
// add edge to this node's adjacency lists
|
||||
void addEdge(Edge<N, E>* e);
|
||||
|
||||
// remove edge from this node's adjacency lists
|
||||
void removeEdge(Edge<N, E>* e);
|
||||
|
||||
N& pl();
|
||||
const N& pl() const;
|
||||
|
||||
private:
|
||||
std::vector<Edge<N, E>*> _adjList;
|
||||
N _pl;
|
||||
|
||||
bool adjContains(const Edge<N, E>* e) const;
|
||||
};
|
||||
|
||||
#include "util/graph/UndirNode.tpp"
|
||||
|
||||
}}
|
||||
|
||||
#endif // UTIL_GRAPH_UNDIRNODE_H_
|
130
src/util/graph/UndirNode.tpp
Normal file
130
src/util/graph/UndirNode.tpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
// Copyright 2016, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
UndirNode<N, E>::UndirNode() : _pl() {
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
UndirNode<N, E>::UndirNode(const N& pl) : _pl(pl) {
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
UndirNode<N, E>::~UndirNode() {
|
||||
// delete self edges
|
||||
for (auto e = _adjList.begin(); e != _adjList.end();) {
|
||||
Edge<N, E>* eP = *e;
|
||||
if (eP->getTo() == this && eP->getFrom() == this) {
|
||||
e = _adjList.erase(e);
|
||||
delete eP;
|
||||
} else {
|
||||
e++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto e = _adjList.begin(); e != _adjList.end(); e++) {
|
||||
Edge<N, E>* eP = *e;
|
||||
|
||||
if (eP->getTo() != this) {
|
||||
eP->getTo()->removeEdge(eP);
|
||||
}
|
||||
|
||||
if (eP->getFrom() != this) {
|
||||
eP->getFrom()->removeEdge(eP);
|
||||
}
|
||||
|
||||
delete eP;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool UndirNode<N, E>::hasEdgeIn(const Edge<N, E>* e) const {
|
||||
return hasEdge(e);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool UndirNode<N, E>::hasEdgeOut(const Edge<N, E>* e) const {
|
||||
return hasEdge(e);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool UndirNode<N, E>::hasEdge(const Edge<N, E>* e) const {
|
||||
return e->getFrom() == this || e->getTo() == this;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
bool UndirNode<N, E>::adjContains(const Edge<N, E>* e) const {
|
||||
for (size_t i = 0; i < _adjList.size(); i++) if (_adjList[i] == e) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void UndirNode<N, E>::addEdge(Edge<N, E>* e) {
|
||||
if (adjContains(e)) return;
|
||||
_adjList.reserve(_adjList.size() + 1);
|
||||
_adjList.push_back(e);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
void UndirNode<N, E>::removeEdge(Edge<N, E>* e) {
|
||||
auto p = std::find(_adjList.begin(), _adjList.end(), e);
|
||||
if (p != _adjList.end()) _adjList.erase(p);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const std::vector<Edge<N, E>*>& UndirNode<N, E>::getAdjList() const {
|
||||
return _adjList;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const std::vector<Edge<N, E>*>& UndirNode<N, E>::getAdjListOut() const {
|
||||
return _adjList;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const std::vector<Edge<N, E>*>& UndirNode<N, E>::getAdjListIn() const {
|
||||
return _adjList;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
size_t UndirNode<N, E>::getDeg() const {
|
||||
return _adjList.size();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
size_t UndirNode<N, E>::getInDeg() const {
|
||||
return getDeg();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
size_t UndirNode<N, E>::getOutDeg() const {
|
||||
return getDeg();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
N& UndirNode<N, E>::pl() {
|
||||
return _pl;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
template <typename N, typename E>
|
||||
const N& UndirNode<N, E>::pl() const {
|
||||
return _pl;
|
||||
}
|
146
src/util/json/JsonWriter.cpp
Normal file
146
src/util/json/JsonWriter.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#include <iomanip>
|
||||
#include "JsonWriter.h"
|
||||
#include "util/String.h"
|
||||
using namespace util;
|
||||
using namespace json;
|
||||
|
||||
using std::ostream;
|
||||
using std::string;
|
||||
using std::map;
|
||||
|
||||
// _____________________________________________________________________________
|
||||
JsonWriter::JsonWriter(std::ostream* out)
|
||||
: _out(out), _pretty(false), _indent(2) {
|
||||
*_out << std::setprecision(10);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
JsonWriter::JsonWriter(std::ostream* out, size_t prec)
|
||||
: _out(out), _pretty(false), _indent(2) {
|
||||
*_out << std::setprecision(prec);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
JsonWriter::JsonWriter(std::ostream* out, size_t prec, bool pret)
|
||||
: _out(out), _pretty(pret), _indent(2) {
|
||||
*_out << std::setprecision(prec);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
JsonWriter::JsonWriter(std::ostream* out, size_t prec, bool pret, size_t indent)
|
||||
: _out(out), _pretty(pret), _indent(indent) {
|
||||
*_out << std::setprecision(prec);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::obj() {
|
||||
if (!_stack.empty() && _stack.top().type == OBJ)
|
||||
throw JsonWriterException("Object not allowed as key");
|
||||
if (!_stack.empty() && _stack.top().type == KEY) _stack.pop();
|
||||
if (!_stack.empty() && _stack.top().type == ARR) valCheck();
|
||||
if (_stack.size() && _stack.top().type == ARR) prettor();
|
||||
(*_out) << "{";
|
||||
_stack.push({OBJ, 1});
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::obj(const std::map<std::string, std::string> kvs) {
|
||||
obj();
|
||||
for (const auto& kv : kvs) {
|
||||
key(kv.first);
|
||||
val(kv.second);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::key(const std::string& k) {
|
||||
if (_stack.empty() || _stack.top().type != OBJ)
|
||||
throw JsonWriterException("Keys only allowed in objects.");
|
||||
if (!_stack.top().empty) (*_out) << "," << (_pretty ? " " : "");
|
||||
_stack.top().empty = 0;
|
||||
prettor();
|
||||
(*_out) << "\"" << k << "\""
|
||||
<< ":" << (_pretty ? " " : "");
|
||||
_stack.push({KEY, 1});
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::keyVal(const std::string& k, const std::string& v) {
|
||||
key(k);
|
||||
val(v);
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::valCheck() {
|
||||
if (_stack.empty() || (_stack.top().type != KEY && _stack.top().type != ARR))
|
||||
throw JsonWriterException("Value not allowed here.");
|
||||
if (!_stack.empty() && _stack.top().type == KEY) _stack.pop();
|
||||
if (!_stack.empty() && _stack.top().type == ARR) {
|
||||
if (!_stack.top().empty) (*_out) << "," << (_pretty ? " " : "");
|
||||
_stack.top().empty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::val(const std::string& v) {
|
||||
valCheck();
|
||||
(*_out) << "\"" << util::jsonStringEscape(v) << "\"";
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::val(int v) {
|
||||
valCheck();
|
||||
(*_out) << v;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::val(double v) {
|
||||
valCheck();
|
||||
(*_out) << v;
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::arr() {
|
||||
if (!_stack.empty() && _stack.top().type == OBJ)
|
||||
throw JsonWriterException("Array not allowed as key");
|
||||
if (!_stack.empty() && _stack.top().type == KEY) _stack.pop();
|
||||
if (!_stack.empty() && _stack.top().type == ARR) valCheck();
|
||||
(*_out) << "[";
|
||||
_stack.push({ARR, 1});
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::prettor() {
|
||||
if (_pretty) {
|
||||
(*_out) << "\n";
|
||||
for (size_t i = 0; i < _indent * _stack.size(); i++) (*_out) << " ";
|
||||
}
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::closeAll() {
|
||||
while (!_stack.empty()) close();
|
||||
}
|
||||
|
||||
// _____________________________________________________________________________
|
||||
void JsonWriter::close() {
|
||||
if (_stack.empty()) return;
|
||||
switch (_stack.top().type) {
|
||||
case OBJ:
|
||||
_stack.pop();
|
||||
prettor();
|
||||
(*_out) << "}";
|
||||
break;
|
||||
case ARR:
|
||||
_stack.pop();
|
||||
(*_out) << "]";
|
||||
break;
|
||||
case KEY:
|
||||
throw JsonWriterException("Missing value.");
|
||||
}
|
||||
}
|
70
src/util/json/JsonWriter.h
Normal file
70
src/util/json/JsonWriter.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2018, University of Freiburg,
|
||||
// Chair of Algorithms and Data Structures.
|
||||
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
|
||||
|
||||
#ifndef UTIL_JSON_JSONWRITER_H_
|
||||
#define UTIL_JSON_JSONWRITER_H_
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
namespace util {
|
||||
namespace json {
|
||||
|
||||
class JsonWriterException : public std::exception {
|
||||
public:
|
||||
JsonWriterException(std::string msg) : _msg(msg) {}
|
||||
~JsonWriterException() throw() {}
|
||||
|
||||
virtual const char* what() const throw() { return _msg.c_str(); };
|
||||
|
||||
private:
|
||||
std::string _msg;
|
||||
};
|
||||
|
||||
// simple JSON writer class without much overhead
|
||||
class JsonWriter {
|
||||
public:
|
||||
explicit JsonWriter(std::ostream* out);
|
||||
JsonWriter(std::ostream* out, size_t prec);
|
||||
JsonWriter(std::ostream* out, size_t prec, bool pretty);
|
||||
JsonWriter(std::ostream* out, size_t prec, bool pretty, size_t indent);
|
||||
~JsonWriter(){};
|
||||
|
||||
void obj();
|
||||
void obj(const std::map<std::string, std::string> kvs);
|
||||
void arr();
|
||||
void key(const std::string& k);
|
||||
void val(const std::string& v);
|
||||
void val(int v);
|
||||
void val(double v);
|
||||
void keyVal(const std::string& k, const std::string& v);
|
||||
|
||||
void close();
|
||||
void closeAll();
|
||||
|
||||
private:
|
||||
std::ostream* _out;
|
||||
|
||||
enum JSON_NODE_T { OBJ, ARR, KEY };
|
||||
|
||||
struct JsonNode {
|
||||
JSON_NODE_T type;
|
||||
bool empty;
|
||||
};
|
||||
|
||||
std::stack<JsonNode> _stack;
|
||||
|
||||
bool _pretty;
|
||||
size_t _indent;
|
||||
|
||||
void valCheck();
|
||||
void prettor();
|
||||
};
|
||||
|
||||
} // namespace json
|
||||
} // namespace util
|
||||
|
||||
#endif // UTIL_JSON_JSONWRITER_H_
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue