Conditional turn restriction support (#3841)
* optionally include condition and via node coords in InputRestrictionContainer * only write conditionals to disk, custom serialization for restrictions * conditional turn lookup, reuse timezone validation from extract-conditionals * adapt updater to use coordinates/osm ids, remove internal to external map * add utc time now parameter to contraction * only compile timezone code where libshp is found, adapt test running * slight refactor, more tests * catch invalid via nodes in restriction parsing, set default cucumber origin to guinée * add another run to test mld routed paths * cosmetic review changes * Simplify Timezoner for windows build * Split declaration and parsing parts for opening hours * adjust conditional tests to run without shapefiles * always include parse conditionals option * Adjust travis timeout * Added dummy TZ shapefile with test timezone polygons * [skip ci] update changelog
This commit is contained in:
		
							parent
							
								
									12f47708cd
								
							
						
					
					
						commit
						799a677e7a
					
				@ -72,7 +72,7 @@ matrix:
 | 
			
		||||
        apt:
 | 
			
		||||
          sources: ['ubuntu-toolchain-r-test']
 | 
			
		||||
          packages: ['g++-6', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev']
 | 
			
		||||
      env: CCOMPILER='gcc-6' CXXCOMPILER='g++-6' BUILD_TYPE='Debug' ENABLE_COVERAGE=ON
 | 
			
		||||
      env: CCOMPILER='gcc-6' CXXCOMPILER='g++-6' BUILD_TYPE='Debug' ENABLE_COVERAGE=ON CUCUMBER_TIMEOUT=20000
 | 
			
		||||
      after_success:
 | 
			
		||||
        - bash <(curl -s https://codecov.io/bash)
 | 
			
		||||
 | 
			
		||||
@ -358,6 +358,9 @@ script:
 | 
			
		||||
    if [ -z "${ENABLE_SANITIZER}" ] && [ "$TARGET_ARCH" != "i686" ]; then
 | 
			
		||||
      npm run nodejs-tests
 | 
			
		||||
    fi
 | 
			
		||||
  - |
 | 
			
		||||
    if [ "${ENABLE_MASON}" == "ON" ]; then
 | 
			
		||||
      npm run test-conditionals
 | 
			
		||||
    fi
 | 
			
		||||
  - popd
 | 
			
		||||
  - yarn test
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,8 @@
 | 
			
		||||
# 5.8.0
 | 
			
		||||
  - Changes from 5.7
 | 
			
		||||
    - Features
 | 
			
		||||
      - Added conditional restriction support with `parse-conditional-restrictions=true|false` to osrm-extract. This option saves conditional turn restrictions to the .restrictions file for parsing by contract later. Added `parse-conditionals-from-now=utc time stamp` and `--time-zone-file=/path/to/file`  to osrm-contract
 | 
			
		||||
 | 
			
		||||
# 5.7.0
 | 
			
		||||
  - Changes from 5.6
 | 
			
		||||
    - Algorithm:
 | 
			
		||||
 | 
			
		||||
@ -448,8 +448,9 @@ if(ENABLE_MASON)
 | 
			
		||||
  set(TBB_LIBRARIES ${MASON_PACKAGE_tbb_LDFLAGS})
 | 
			
		||||
 | 
			
		||||
  mason_use(libshp2 VERSION ${MASON_LIBSHP_VERSION})
 | 
			
		||||
  set(LIBSHAPEFILE_INCLUDE_DIR ${MASON_PACKAGE_libshp2_INCLUDE_DIRS})
 | 
			
		||||
  set(LIBSHAPEFILE_LIBRARY ${MASON_PACKAGE_libshp2_LDFLAGS})
 | 
			
		||||
  target_include_directories(UTIL PRIVATE ${MASON_PACKAGE_libshp2_INCLUDE_DIRS})
 | 
			
		||||
  set(MAYBE_SHAPEFILE ${MASON_PACKAGE_libshp2_STATIC_LIBS})
 | 
			
		||||
  target_compile_definitions(UTIL PRIVATE COMPILE_DEFINITIONS ENABLE_SHAPEFILE)
 | 
			
		||||
 | 
			
		||||
  if(NOT MASON_PACKAGE_tbb_LIBRARY_DIRS)
 | 
			
		||||
    message(FATAL_ERROR "MASON_PACKAGE_tbb_LIBRARY_DIRS is empty, rpath will not work")
 | 
			
		||||
@ -524,6 +525,15 @@ else()
 | 
			
		||||
    ENDIF()
 | 
			
		||||
  ENDIF()
 | 
			
		||||
 | 
			
		||||
  find_package(Shapefile) # optional package libshp-dev
 | 
			
		||||
  if (Shapefile_FOUND)
 | 
			
		||||
    set(MAYBE_SHAPEFILE "${LIBSHAPEFILE_LIBRARY}")
 | 
			
		||||
    target_include_directories(UTIL PRIVATE ${LIBSHAPEFILE_INCLUDE_DIR})
 | 
			
		||||
    target_compile_definitions(UTIL PRIVATE COMPILE_DEFINITIONS ENABLE_SHAPEFILE)
 | 
			
		||||
  else()
 | 
			
		||||
    set(MAYBE_SHAPEFILE "")
 | 
			
		||||
  endif()
 | 
			
		||||
 | 
			
		||||
  set(USED_LUA_LIBRARIES ${LUA_LIBRARIES})
 | 
			
		||||
  add_dependency_includes(${LUA_INCLUDE_DIR})
 | 
			
		||||
 | 
			
		||||
@ -600,12 +610,12 @@ set(BOOST_ENGINE_LIBRARIES
 | 
			
		||||
  ${BOOST_BASE_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
# Binaries
 | 
			
		||||
target_link_libraries(osrm-datastore osrm_store ${Boost_PROGRAM_OPTIONS_LIBRARY})
 | 
			
		||||
target_link_libraries(osrm-datastore osrm_store ${Boost_PROGRAM_OPTIONS_LIBRARY} ${MAYBE_SHAPEFILE})
 | 
			
		||||
target_link_libraries(osrm-extract osrm_extract ${Boost_PROGRAM_OPTIONS_LIBRARY})
 | 
			
		||||
target_link_libraries(osrm-partition osrm_partition ${Boost_PROGRAM_OPTIONS_LIBRARY})
 | 
			
		||||
target_link_libraries(osrm-customize osrm_customize ${Boost_PROGRAM_OPTIONS_LIBRARY})
 | 
			
		||||
target_link_libraries(osrm-contract osrm_contract ${Boost_PROGRAM_OPTIONS_LIBRARY})
 | 
			
		||||
target_link_libraries(osrm-routed osrm ${Boost_PROGRAM_OPTIONS_LIBRARY} ${OPTIONAL_SOCKET_LIBS} ${ZLIB_LIBRARY})
 | 
			
		||||
target_link_libraries(osrm-routed osrm ${Boost_PROGRAM_OPTIONS_LIBRARY} ${MAYBE_SHAPEFILE} ${OPTIONAL_SOCKET_LIBS} ${ZLIB_LIBRARY})
 | 
			
		||||
 | 
			
		||||
set(EXTRACTOR_LIBRARIES
 | 
			
		||||
    ${BZIP2_LIBRARIES}
 | 
			
		||||
@ -630,6 +640,7 @@ set(CUSTOMIZER_LIBRARIES
 | 
			
		||||
    ${BOOST_ENGINE_LIBRARIES}
 | 
			
		||||
    ${CMAKE_THREAD_LIBS_INIT}
 | 
			
		||||
    ${TBB_LIBRARIES}
 | 
			
		||||
    ${MAYBE_SHAPEFILE}
 | 
			
		||||
    ${MAYBE_RT_LIBRARY}
 | 
			
		||||
    ${MAYBE_COVERAGE_LIBRARIES})
 | 
			
		||||
set(UPDATER_LIBRARIES
 | 
			
		||||
@ -637,13 +648,16 @@ set(UPDATER_LIBRARIES
 | 
			
		||||
    ${CMAKE_THREAD_LIBS_INIT}
 | 
			
		||||
    ${TBB_LIBRARIES}
 | 
			
		||||
    ${MAYBE_RT_LIBRARY}
 | 
			
		||||
    ${MAYBE_COVERAGE_LIBRARIES})
 | 
			
		||||
    ${MAYBE_SHAPEFILE}
 | 
			
		||||
    ${MAYBE_COVERAGE_LIBRARIES}
 | 
			
		||||
    ${ZLIB_LIBRARY})
 | 
			
		||||
set(CONTRACTOR_LIBRARIES
 | 
			
		||||
    ${BOOST_BASE_LIBRARIES}
 | 
			
		||||
    ${CMAKE_THREAD_LIBS_INIT}
 | 
			
		||||
    ${USED_LUA_LIBRARIES}
 | 
			
		||||
    ${STXXL_LIBRARY}
 | 
			
		||||
    ${TBB_LIBRARIES}
 | 
			
		||||
    ${MAYBE_SHAPEFILE}
 | 
			
		||||
    ${MAYBE_RT_LIBRARY}
 | 
			
		||||
    ${MAYBE_COVERAGE_LIBRARIES})
 | 
			
		||||
set(ENGINE_LIBRARIES
 | 
			
		||||
@ -664,7 +678,9 @@ set(UTIL_LIBRARIES
 | 
			
		||||
    ${CMAKE_THREAD_LIBS_INIT}
 | 
			
		||||
    ${STXXL_LIBRARY}
 | 
			
		||||
    ${TBB_LIBRARIES}
 | 
			
		||||
    ${MAYBE_SHAPEFILE}
 | 
			
		||||
    ${MAYBE_COVERAGE_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
# Libraries
 | 
			
		||||
target_link_libraries(osrm ${ENGINE_LIBRARIES})
 | 
			
		||||
target_link_libraries(osrm_update ${UPDATER_LIBRARIES})
 | 
			
		||||
@ -676,22 +692,21 @@ target_link_libraries(osrm_store ${STORAGE_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
# BUILD_COMPONENTS
 | 
			
		||||
add_executable(osrm-components src/tools/components.cpp $<TARGET_OBJECTS:UTIL>)
 | 
			
		||||
target_link_libraries(osrm-components ${TBB_LIBRARIES} ${BOOST_BASE_LIBRARIES})
 | 
			
		||||
target_link_libraries(osrm-components ${TBB_LIBRARIES} ${BOOST_BASE_LIBRARIES} ${UTIL_LIBRARIES})
 | 
			
		||||
install(TARGETS osrm-components DESTINATION bin)
 | 
			
		||||
 | 
			
		||||
if(BUILD_TOOLS)
 | 
			
		||||
  message(STATUS "Activating OSRM internal tools")
 | 
			
		||||
  add_executable(osrm-io-benchmark src/tools/io-benchmark.cpp $<TARGET_OBJECTS:UTIL>)
 | 
			
		||||
  target_link_libraries(osrm-io-benchmark ${BOOST_BASE_LIBRARIES})
 | 
			
		||||
  target_link_libraries(osrm-io-benchmark ${BOOST_BASE_LIBRARIES} ${MAYBE_SHAPEFILE})
 | 
			
		||||
 | 
			
		||||
  install(TARGETS osrm-io-benchmark DESTINATION bin)
 | 
			
		||||
 | 
			
		||||
  find_package(Shapefile) # package libshp-dev
 | 
			
		||||
  if(SHAPEFILE_FOUND AND (Boost_VERSION VERSION_GREATER 106000 OR ENABLE_MASON))
 | 
			
		||||
    add_executable(osrm-extract-conditionals src/tools/extract-conditionals.cpp $<TARGET_OBJECTS:UTIL>)
 | 
			
		||||
    target_include_directories(osrm-extract-conditionals PRIVATE ${LIBSHAPEFILE_INCLUDE_DIR})
 | 
			
		||||
    target_link_libraries(osrm-extract-conditionals ${OSMIUM_LIBRARIES} ${BOOST_BASE_LIBRARIES} ${Boost_PROGRAM_OPTIONS_LIBRARY}
 | 
			
		||||
      ${LIBSHAPEFILE_LIBRARY} ${BZIP2_LIBRARIES} ${ZLIB_LIBRARY} ${EXPAT_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
 | 
			
		||||
        ${UTIL_LIBRARIES} ${BZIP2_LIBRARIES} ${ZLIB_LIBRARY} ${EXPAT_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
 | 
			
		||||
    install(TARGETS osrm-extract-conditionals DESTINATION bin)
 | 
			
		||||
  endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,9 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
    default: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions',
 | 
			
		||||
    verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions',
 | 
			
		||||
    default: '--strict --tags ~@stress --tags ~@todo --tags ~@shapelib --require features/support --require features/step_definitions',
 | 
			
		||||
    verify: '--strict --tags ~@stress --tags ~@todo --tags ~@shapelib -f progress --require features/support --require features/step_definitions',
 | 
			
		||||
    todo: '--strict --tags @todo --require features/support --require features/step_definitions',
 | 
			
		||||
    all: '--strict --require features/support --require features/step_definitions',
 | 
			
		||||
    mld: '--strict --tags ~@stress --tags ~@todo --tags ~@alternative --tags ~@matrix --tags ~@trip --require features/support --require features/step_definitions -f progress'
 | 
			
		||||
    mld: '--strict --tags ~@stress --tags ~@todo --tags ~@alternative --tags ~@matrix --tags ~@trip --tags ~@shapelib --require features/support --require features/step_definitions -f progress',
 | 
			
		||||
    conditionals: '--strict --tags @conditionals --require features/support --require features/step_definitions',
 | 
			
		||||
    mld_conditionals: '--strict --tags @conditionals --require features/support --require features/step_definitions -f progress'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										702
									
								
								features/car/conditional_restrictions.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										702
									
								
								features/car/conditional_restrictions.feature
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,702 @@
 | 
			
		||||
@routing @car @restrictions
 | 
			
		||||
Feature: Car - Turn restrictions
 | 
			
		||||
# Handle turn restrictions as defined by http://wiki.openstreetmap.org/wiki/Relation:restriction
 | 
			
		||||
# Note that if u-turns are allowed, turn restrictions can lead to suprising, but correct, routes.
 | 
			
		||||
 | 
			
		||||
    Background: Use car routing
 | 
			
		||||
        Given the profile "car"
 | 
			
		||||
        Given a grid size of 200 meters
 | 
			
		||||
        Given the origin -9.2972,10.3811
 | 
			
		||||
        # coordinate in Guinée, a country that observes GMT year round
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - ignores unrecognized restriction
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional                |
 | 
			
		||||
            | restriction | ej       | nj     | j        | only_right_turn @ (has_pygmies > 10 p) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route    |
 | 
			
		||||
            | e    | s  | ej,js,js |
 | 
			
		||||
            | e    | n  | ej,nj,nj |
 | 
			
		||||
            | e    | p  | ej,jp,jp |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - Restriction would be on, but the restriction was badly tagged
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
           p  |
 | 
			
		||||
            \ |
 | 
			
		||||
              j
 | 
			
		||||
              | \
 | 
			
		||||
              s  m
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes |
 | 
			
		||||
            | nj    |
 | 
			
		||||
            | js    |
 | 
			
		||||
            | pjm   |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional             |
 | 
			
		||||
            | restriction | nj       | pjm    | j        | no_left_turn @ (Mo-Fr 07:00-10:30)  |
 | 
			
		||||
            | restriction | js       | pjm    | j        | no_right_turn @ (Mo-Fr 07:00-10:30) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route      |
 | 
			
		||||
            | n    | m  | nj,pjm,pjm |
 | 
			
		||||
            | s    | m  | js,pjm,pjm |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - ignores except restriction
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | no     |
 | 
			
		||||
            | jp    | no     |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional               | except   |
 | 
			
		||||
            | restriction | ej       | nj     | j        | only_right_turn @ (Mo-Su 08:00-12:00) | motorcar |
 | 
			
		||||
            | restriction | jp       | nj     | j        | only_left_turn @ (Mo-Su 08:00-12:00)  | bus      |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          | # |
 | 
			
		||||
            | e    | s  | ej,js,js       |   |
 | 
			
		||||
            | e    | n  | ej,nj,nj       | restriction does not apply to cars |
 | 
			
		||||
            | e    | p  | ej,jp,jp       |   |
 | 
			
		||||
            | p    | s  | jp,nj,nj,js,js | restriction excepting busses still applies to cars  |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - only_right_turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional               |
 | 
			
		||||
            | restriction | ej       | nj     | j        | only_right_turn @ (Mo-Su 07:00-14:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,nj,nj,js,js |
 | 
			
		||||
            | e    | n  | ej,nj,nj       |
 | 
			
		||||
            | e    | p  | ej,nj,nj,jp,jp |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - No right turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional               |
 | 
			
		||||
            | restriction | ej       | nj     | j        | no_right_turn @ (Mo-Fr 07:00-13:00)   |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          | # |
 | 
			
		||||
            | e    | s  | ej,js,js       | normal turn |
 | 
			
		||||
            | e    | n  | ej,js,js,nj,nj | avoids right turn |
 | 
			
		||||
            | e    | p  | ej,jp,jp       | normal maneuver |
 | 
			
		||||
 | 
			
		||||
    @only_turning @conditionals
 | 
			
		||||
    Scenario: Car - only_left_turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional              |
 | 
			
		||||
            | restriction | ej       | js     | j        | only_left_turn @ (Mo-Fr 07:00-16:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,js,js       |
 | 
			
		||||
            | e    | n  | ej,js,js,nj,nj |
 | 
			
		||||
            | e    | p  | ej,js,js,jp,jp |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - No left turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional            |
 | 
			
		||||
            | restriction | ej       | js     | j        | no_left_turn @ (Mo-Su 00:00-23:59) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,nj,nj,js,js |
 | 
			
		||||
            | e    | n  | ej,nj,nj       |
 | 
			
		||||
            | e    | p  | ej,jp,jp       |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - Conditional restriction is off
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional             |
 | 
			
		||||
            | restriction | ej       | nj     | j        | no_right_turn @ (Mo-Su 16:00-20:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route    |
 | 
			
		||||
            | e    | s  | ej,js,js |
 | 
			
		||||
            | e    | n  | ej,nj,nj |
 | 
			
		||||
            | e    | p  | ej,jp,jp |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - Conditional restriction is on
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
        # 10am utc, wed
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493805600"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493805600"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional             |
 | 
			
		||||
            | restriction | ej       | nj     | j        | no_right_turn @ (Mo-Fr 07:00-14:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,js,js       |
 | 
			
		||||
            | e    | n  | ej,js,js,nj,nj |
 | 
			
		||||
            | e    | p  | ej,jp,jp       |
 | 
			
		||||
 | 
			
		||||
    @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - Conditional restriction with multiple time windows
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
        # 5pm Wed 02 May, 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493744400"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493744400"
 | 
			
		||||
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
           p  |
 | 
			
		||||
            \ |
 | 
			
		||||
              j
 | 
			
		||||
              | \
 | 
			
		||||
              s  m
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
            | mj    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional                         |
 | 
			
		||||
            | restriction | nj       | jp     | j        | no_right_turn @ (Mo-Fr 07:00-11:00,16:00-18:30) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | n    | p  | nj,js,js,jp,jp |
 | 
			
		||||
            | m    | p  | mj,jp,jp       |
 | 
			
		||||
 | 
			
		||||
    @only_turning @conditionals
 | 
			
		||||
    Scenario: Car - Somewhere in Liverpool, the UK, GMT timezone
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions=1"
 | 
			
		||||
        # 9am UTC, 10am BST
 | 
			
		||||
        Given the contract extra arguments "--parse-conditionals-from-now=1493802000"
 | 
			
		||||
        Given the customize extra arguments "--parse-conditionals-from-now=1493802000"
 | 
			
		||||
 | 
			
		||||
        #    """
 | 
			
		||||
        #     a
 | 
			
		||||
        #          e
 | 
			
		||||
        #      b
 | 
			
		||||
        #   d
 | 
			
		||||
        #       c
 | 
			
		||||
        #    """
 | 
			
		||||
        Given the node locations
 | 
			
		||||
            | node | lat     | lon     |
 | 
			
		||||
            | a    | 51.5250 | -0.1166 |
 | 
			
		||||
            | b    | 51.5243 | -0.1159 |
 | 
			
		||||
            | c    | 51.5238 | -0.1152 |
 | 
			
		||||
            | d    | 51.5241 | -0.1167 |
 | 
			
		||||
            | e    | 51.5247 | -0.1153 |
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | name  |
 | 
			
		||||
            | ab    | albic |
 | 
			
		||||
            | bc    | albic |
 | 
			
		||||
            | db    | dobe |
 | 
			
		||||
            | be    | dobe |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from  | way:to  | node:via | restriction:conditional               |
 | 
			
		||||
            | restriction | ab        | be      | b        | only_left_turn @ (Mo-Fr 07:00-11:00)  |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route                       | turns                                        |
 | 
			
		||||
            | a    | c  | albic,dobe,dobe,albic,albic | depart,turn left,turn uturn,turn left,arrive |
 | 
			
		||||
            | a    | e  | albic,dobe,dobe             | depart,turn left,arrive                      |
 | 
			
		||||
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - only_right_turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional               |
 | 
			
		||||
            | restriction | ej       | nj     | j        | only_right_turn @ (Mo-Su 07:00-14:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,nj,nj,js,js |
 | 
			
		||||
            | e    | n  | ej,nj,nj       |
 | 
			
		||||
            | e    | p  | ej,nj,nj,jp,jp |
 | 
			
		||||
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - No right turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional               |
 | 
			
		||||
            | restriction | ej       | nj     | j        | no_right_turn @ (Mo-Fr 07:00-13:00)   |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          | # |
 | 
			
		||||
            | e    | s  | ej,js,js       | normal turn |
 | 
			
		||||
            | e    | n  | ej,js,js,nj,nj | avoids right turn |
 | 
			
		||||
            | e    | p  | ej,jp,jp       | normal maneuver |
 | 
			
		||||
 | 
			
		||||
    @shapelib @only_turning @conditionals
 | 
			
		||||
    Scenario: Car - only_left_turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional              |
 | 
			
		||||
            | restriction | ej       | js     | j        | only_left_turn @ (Mo-Fr 07:00-16:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,js,js       |
 | 
			
		||||
            | e    | n  | ej,js,js,nj,nj |
 | 
			
		||||
            | e    | p  | ej,js,js,jp,jp |
 | 
			
		||||
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - No left turn
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional            |
 | 
			
		||||
            | restriction | ej       | js     | j        | no_left_turn @ (Mo-Su 00:00-23:59) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,nj,nj,js,js |
 | 
			
		||||
            | e    | n  | ej,nj,nj       |
 | 
			
		||||
            | e    | p  | ej,jp,jp       |
 | 
			
		||||
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - Conditional restriction is off
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
                                            # time stamp for 10am on Tues, 02 May 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493719200"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional             |
 | 
			
		||||
            | restriction | ej       | nj     | j        | no_right_turn @ (Mo-Su 16:00-20:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route    |
 | 
			
		||||
            | e    | s  | ej,js,js |
 | 
			
		||||
            | e    | n  | ej,nj,nj |
 | 
			
		||||
            | e    | p  | ej,jp,jp |
 | 
			
		||||
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - Conditional restriction is on
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
        # 10am utc, wed
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493805600"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493805600"
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
            p j e
 | 
			
		||||
              s
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | ej    | yes    |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional             |
 | 
			
		||||
            | restriction | ej       | nj     | j        | no_right_turn @ (Mo-Fr 07:00-14:00) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | e    | s  | ej,js,js       |
 | 
			
		||||
            | e    | n  | ej,js,js,nj,nj |
 | 
			
		||||
            | e    | p  | ej,jp,jp       |
 | 
			
		||||
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - Conditional restriction with multiple time windows
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions"
 | 
			
		||||
        # 5pm Wed 02 May, 2017 GMT
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493744400"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493744400"
 | 
			
		||||
 | 
			
		||||
        Given the node map
 | 
			
		||||
            """
 | 
			
		||||
              n
 | 
			
		||||
           p  |
 | 
			
		||||
            \ |
 | 
			
		||||
              j
 | 
			
		||||
              | \
 | 
			
		||||
              s  m
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway |
 | 
			
		||||
            | nj    | no     |
 | 
			
		||||
            | js    | no     |
 | 
			
		||||
            | jp    | yes    |
 | 
			
		||||
            | mj    | yes    |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from | way:to | node:via | restriction:conditional                         |
 | 
			
		||||
            | restriction | nj       | jp     | j        | no_right_turn @ (Mo-Fr 07:00-11:00,16:00-18:30) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route          |
 | 
			
		||||
            | n    | p  | nj,js,js,jp,jp |
 | 
			
		||||
            | m    | p  | mj,jp,jp       |
 | 
			
		||||
 | 
			
		||||
    # https://www.openstreetmap.org/#map=18/38.91099/-77.00888
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - DC North capitol situation, two on one off
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions=1"
 | 
			
		||||
        # 9pm Wed 02 May, 2017 UTC, 5pm EST
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493845200"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493845200"
 | 
			
		||||
 | 
			
		||||
        #    """
 | 
			
		||||
        #      a h
 | 
			
		||||
        #   d
 | 
			
		||||
        #      b g
 | 
			
		||||
        #           e
 | 
			
		||||
        #      c f
 | 
			
		||||
        #    """
 | 
			
		||||
        Given the node locations
 | 
			
		||||
            | node | lat     | lon      |
 | 
			
		||||
            | a    | 38.9113 | -77.0091 |
 | 
			
		||||
            | b    | 38.9108 | -77.0091 |
 | 
			
		||||
            | c    | 38.9104 | -77.0091 |
 | 
			
		||||
            | d    | 38.9110 | -77.0096 |
 | 
			
		||||
            | e    | 38.9106 | -77.0086 |
 | 
			
		||||
            | f    | 38.9105 | -77.0090 |
 | 
			
		||||
            | g    | 38.9108 | -77.0090 |
 | 
			
		||||
            | h    | 38.9113 | -77.0090 |
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway | name       |
 | 
			
		||||
            | ab    | yes    | cap south  |
 | 
			
		||||
            | bc    | yes    | cap south  |
 | 
			
		||||
            | fg    | yes    | cap north  |
 | 
			
		||||
            | gh    | yes    | cap north  |
 | 
			
		||||
            | db    | no     | florida nw |
 | 
			
		||||
            | bg    | no     | florida    |
 | 
			
		||||
            | ge    | no     | florida ne |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from  | way:to  | node:via | restriction:conditional                        |
 | 
			
		||||
            | restriction | ab        | bg      | b        | no_left_turn @ (Mo-Fr 07:00-09:30,16:00-18:30) |
 | 
			
		||||
            | restriction | fg        | bg      | g        | no_left_turn @ (Mo-Fr 06:00-10:00)             |
 | 
			
		||||
            | restriction | bg        | bc      | b        | no_left_turn @ (Mo-Fr 07:00-09:30,16:00-18:30) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route                                      | turns                                   |
 | 
			
		||||
            | a    | e  | cap south,florida nw,florida nw,florida ne | depart,turn right,turn uturn,arrive     |
 | 
			
		||||
            | f    | d  | cap north,florida,florida nw               | depart,turn left,arrive                 |
 | 
			
		||||
            | e    | c  | florida ne,florida nw,cap south,cap south  | depart,continue uturn,turn right,arrive |
 | 
			
		||||
 | 
			
		||||
    @shapelib @no_turning @conditionals
 | 
			
		||||
    Scenario: Car - DC North capitol situation, one on two off
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions=1"
 | 
			
		||||
        # 10:30am utc, wed, 6:30am est
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493807400"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493807400"
 | 
			
		||||
 | 
			
		||||
        #    """
 | 
			
		||||
        #      a h
 | 
			
		||||
        #   d
 | 
			
		||||
        #      b g
 | 
			
		||||
        #           e
 | 
			
		||||
        #      c f
 | 
			
		||||
        #    """
 | 
			
		||||
        Given the node locations
 | 
			
		||||
            | node | lat     | lon      |
 | 
			
		||||
            | a    | 38.9113 | -77.0091 |
 | 
			
		||||
            | b    | 38.9108 | -77.0091 |
 | 
			
		||||
            | c    | 38.9104 | -77.0091 |
 | 
			
		||||
            | d    | 38.9110 | -77.0096 |
 | 
			
		||||
            | e    | 38.9106 | -77.0086 |
 | 
			
		||||
            | f    | 38.9105 | -77.0090 |
 | 
			
		||||
            | g    | 38.9108 | -77.0090 |
 | 
			
		||||
            | h    | 38.9113 | -77.0090 |
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | oneway | name       |
 | 
			
		||||
            | ab    | yes    | cap south  |
 | 
			
		||||
            | bc    | yes    | cap south  |
 | 
			
		||||
            | fg    | yes    | cap north  |
 | 
			
		||||
            | gh    | yes    | cap north  |
 | 
			
		||||
            | db    | no     | florida nw |
 | 
			
		||||
            | bg    | no     | florida    |
 | 
			
		||||
            | ge    | no     | florida ne |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from  | way:to  | node:via | restriction:conditional                        |
 | 
			
		||||
            | restriction | ab        | bg      | b        | no_left_turn @ (Mo-Fr 07:00-09:30,16:00-18:30) |
 | 
			
		||||
            | restriction | fg        | bg      | g        | no_left_turn @ (Mo-Fr 06:00-10:00)             |
 | 
			
		||||
            | restriction | bg        | bc      | b        | no_left_turn @ (Mo-Fr 07:00-09:30,16:00-18:30) |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route                                      | turns                                     |
 | 
			
		||||
            | a    | e  | cap south,florida,florida ne               | depart,turn left,arrive                   |
 | 
			
		||||
            | f    | d  | cap north,florida ne,florida ne,florida nw | depart,turn sharp right,turn uturn,arrive |
 | 
			
		||||
            | e    | c  | florida ne,cap south,cap south             | depart,turn left,arrive                   |
 | 
			
		||||
 | 
			
		||||
    @shapelib @only_turning @conditionals
 | 
			
		||||
    Scenario: Car - Somewhere in Liverpool, the UK, GMT timezone
 | 
			
		||||
        Given the extract extra arguments "--parse-conditional-restrictions=1"
 | 
			
		||||
        # 9am UTC, 10am BST
 | 
			
		||||
        Given the contract extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493802000"
 | 
			
		||||
        Given the customize extra arguments "--time-zone-file=test/data/tz/OGRGeoJSON.shp --parse-conditionals-from-now=1493802000"
 | 
			
		||||
 | 
			
		||||
        #    """
 | 
			
		||||
        #     a
 | 
			
		||||
        #          e
 | 
			
		||||
        #      b
 | 
			
		||||
        #   d
 | 
			
		||||
        #       c
 | 
			
		||||
        #    """
 | 
			
		||||
        Given the node locations
 | 
			
		||||
            | node | lat     | lon     |
 | 
			
		||||
            | a    | 51.5250 | -0.1166 |
 | 
			
		||||
            | b    | 51.5243 | -0.1159 |
 | 
			
		||||
            | c    | 51.5238 | -0.1152 |
 | 
			
		||||
            | d    | 51.5241 | -0.1167 |
 | 
			
		||||
            | e    | 51.5247 | -0.1153 |
 | 
			
		||||
 | 
			
		||||
        And the ways
 | 
			
		||||
            | nodes | name  |
 | 
			
		||||
            | ab    | albic |
 | 
			
		||||
            | bc    | albic |
 | 
			
		||||
            | db    | dobe |
 | 
			
		||||
            | be    | dobe |
 | 
			
		||||
 | 
			
		||||
        And the relations
 | 
			
		||||
            | type        | way:from  | way:to  | node:via | restriction:conditional               |
 | 
			
		||||
            | restriction | ab        | be      | b        | only_left_turn @ (Mo-Fr 07:00-11:00)  |
 | 
			
		||||
 | 
			
		||||
        When I route I should get
 | 
			
		||||
            | from | to | route                       | turns                                        |
 | 
			
		||||
            | a    | c  | albic,dobe,dobe,albic,albic | depart,turn left,turn uturn,turn left,arrive |
 | 
			
		||||
            | a    | e  | albic,dobe,dobe             | depart,turn left,arrive                      |
 | 
			
		||||
@ -38,7 +38,7 @@ module.exports = function () {
 | 
			
		||||
        callback();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.Given(/^the origin ([-+]?[0-9]*\.?[0-9]+),([-+]?[0-9]*\.?[0-9]+)$/, (lat, lon, callback) => {
 | 
			
		||||
    this.Given(/^the origin ([-+]?[0-9]*\.?[0-9]+),([-+]?[0-9]*\.?[0-9]+)$/, (lon, lat, callback) => {
 | 
			
		||||
        this.setOrigin([parseFloat(lon), parseFloat(lat)]);
 | 
			
		||||
        callback();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ module.exports = function () {
 | 
			
		||||
        this.WAY_SPACING = 100;
 | 
			
		||||
        this.DEFAULT_GRID_SIZE = 100; // meters
 | 
			
		||||
        // get algorithm name from the command line profile argument
 | 
			
		||||
        this.ROUTING_ALGORITHM = process.argv[process.argv.indexOf('-p') + 1] === 'mld' ? 'MLD' : 'CH';
 | 
			
		||||
        this.ROUTING_ALGORITHM = process.argv[process.argv.indexOf('-p') + 1].match('mld') ? 'MLD' : 'CH';
 | 
			
		||||
 | 
			
		||||
        this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000;
 | 
			
		||||
        this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT;
 | 
			
		||||
 | 
			
		||||
@ -52,13 +52,13 @@ namespace lookup
 | 
			
		||||
#pragma pack(push, 1)
 | 
			
		||||
struct TurnIndexBlock
 | 
			
		||||
{
 | 
			
		||||
    OSMNodeID from_id;
 | 
			
		||||
    OSMNodeID via_id;
 | 
			
		||||
    OSMNodeID to_id;
 | 
			
		||||
    NodeID from_id;
 | 
			
		||||
    NodeID via_id;
 | 
			
		||||
    NodeID to_id;
 | 
			
		||||
};
 | 
			
		||||
#pragma pack(pop)
 | 
			
		||||
static_assert(std::is_trivial<TurnIndexBlock>::value, "TurnIndexBlock is not trivial");
 | 
			
		||||
static_assert(sizeof(TurnIndexBlock) == 24, "TurnIndexBlock is not packed correctly");
 | 
			
		||||
static_assert(sizeof(TurnIndexBlock) == 12, "TurnIndexBlock is not packed correctly");
 | 
			
		||||
} // ns lookup
 | 
			
		||||
 | 
			
		||||
struct NodeBasedGraphToEdgeBasedGraphMappingWriter; // fwd. decl
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,7 @@ class ExtractionContainers
 | 
			
		||||
    void PrepareEdges(ScriptingEnvironment &scripting_environment);
 | 
			
		||||
 | 
			
		||||
    void WriteNodes(storage::io::FileWriter &file_out) const;
 | 
			
		||||
    void WriteRestrictions(const std::string &restrictions_file_name) const;
 | 
			
		||||
    void WriteRestrictions(const std::string &restrictions_file_name);
 | 
			
		||||
    void WriteEdges(storage::io::FileWriter &file_out) const;
 | 
			
		||||
    void WriteCharData(const std::string &file_name);
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ class ExtractionContainers
 | 
			
		||||
    using STXXLNodeIDVector = stxxl::vector<OSMNodeID>;
 | 
			
		||||
    using STXXLNodeVector = stxxl::vector<ExternalMemoryNode>;
 | 
			
		||||
    using STXXLEdgeVector = stxxl::vector<InternalExtractorEdge>;
 | 
			
		||||
    using STXXLRestrictionsVector = stxxl::vector<InputRestrictionContainer>;
 | 
			
		||||
    using RestrictionsVector = std::vector<InputRestrictionContainer>;
 | 
			
		||||
    using STXXLWayIDStartEndVector = stxxl::vector<FirstAndLastSegmentOfWay>;
 | 
			
		||||
    using STXXLNameCharData = stxxl::vector<unsigned char>;
 | 
			
		||||
    using STXXLNameOffsets = stxxl::vector<unsigned>;
 | 
			
		||||
@ -59,10 +59,11 @@ class ExtractionContainers
 | 
			
		||||
    STXXLNameCharData name_char_data;
 | 
			
		||||
    STXXLNameOffsets name_offsets;
 | 
			
		||||
    // an adjacency array containing all turn lane masks
 | 
			
		||||
    STXXLRestrictionsVector restrictions_list;
 | 
			
		||||
    RestrictionsVector restrictions_list;
 | 
			
		||||
    STXXLWayIDStartEndVector way_start_end_id_list;
 | 
			
		||||
    std::unordered_map<OSMNodeID, NodeID> external_to_internal_node_id_map;
 | 
			
		||||
    unsigned max_internal_node_id;
 | 
			
		||||
    std::vector<TurnRestriction> unconditional_turn_restrictions;
 | 
			
		||||
 | 
			
		||||
    ExtractionContainers();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,9 @@ class Extractor
 | 
			
		||||
  private:
 | 
			
		||||
    ExtractorConfig config;
 | 
			
		||||
 | 
			
		||||
    std::vector<TurnRestriction> ParseOSMData(ScriptingEnvironment &scripting_environment,
 | 
			
		||||
                                              const unsigned number_of_threads);
 | 
			
		||||
 | 
			
		||||
    std::pair<std::size_t, EdgeID>
 | 
			
		||||
    BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
 | 
			
		||||
                           std::vector<util::Coordinate> &coordinates,
 | 
			
		||||
@ -64,7 +67,8 @@ class Extractor
 | 
			
		||||
                           std::vector<bool> &node_is_startpoint,
 | 
			
		||||
                           std::vector<EdgeWeight> &edge_based_node_weights,
 | 
			
		||||
                           util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
 | 
			
		||||
                           const std::string &intersection_class_output_file);
 | 
			
		||||
                           const std::string &intersection_class_output_file,
 | 
			
		||||
                           std::vector<TurnRestriction> &turn_restrictions);
 | 
			
		||||
    void WriteProfileProperties(const std::string &output_path,
 | 
			
		||||
                                const ProfileProperties &properties) const;
 | 
			
		||||
    void FindComponents(unsigned max_edge_id,
 | 
			
		||||
 | 
			
		||||
@ -112,6 +112,7 @@ struct ExtractorConfig
 | 
			
		||||
    std::string turn_penalties_index_path;
 | 
			
		||||
 | 
			
		||||
    bool use_metadata;
 | 
			
		||||
    bool parse_conditionals;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
#ifndef RESTRICTION_HPP
 | 
			
		||||
#define RESTRICTION_HPP
 | 
			
		||||
 | 
			
		||||
#include "util/coordinate.hpp"
 | 
			
		||||
#include "util/opening_hours.hpp"
 | 
			
		||||
#include "util/typedefs.hpp"
 | 
			
		||||
 | 
			
		||||
#include <limits>
 | 
			
		||||
@ -20,6 +22,8 @@ struct TurnRestriction
 | 
			
		||||
    WayOrNode from;
 | 
			
		||||
    WayOrNode to;
 | 
			
		||||
 | 
			
		||||
    std::vector<util::OpeningHours> condition;
 | 
			
		||||
 | 
			
		||||
    struct Bits
 | 
			
		||||
    { // mostly unused
 | 
			
		||||
        Bits()
 | 
			
		||||
 | 
			
		||||
@ -95,7 +95,7 @@ class RestrictionMap
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // find all potential start edges. It is more efficent to get a (small) list
 | 
			
		||||
        // find all potential start edges. It is more efficient to get a (small) list
 | 
			
		||||
        // of potential start edges than iterating over all buckets
 | 
			
		||||
        std::vector<NodeID> predecessors;
 | 
			
		||||
        for (const EdgeID current_edge_id : graph.GetAdjacentEdgeRange(node_u))
 | 
			
		||||
 | 
			
		||||
@ -41,14 +41,17 @@ class ScriptingEnvironment;
 | 
			
		||||
class RestrictionParser
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
    RestrictionParser(ScriptingEnvironment &scripting_environment);
 | 
			
		||||
    boost::optional<InputRestrictionContainer> TryParse(const osmium::Relation &relation) const;
 | 
			
		||||
    RestrictionParser(bool use_turn_restrictions,
 | 
			
		||||
                      bool parse_conditionals,
 | 
			
		||||
                      std::vector<std::string> &restrictions);
 | 
			
		||||
    std::vector<InputRestrictionContainer> TryParse(const osmium::Relation &relation) const;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    bool ShouldIgnoreRestriction(const std::string &except_tag_string) const;
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> restrictions;
 | 
			
		||||
    bool use_turn_restrictions;
 | 
			
		||||
    bool parse_conditionals;
 | 
			
		||||
    std::vector<std::string> restrictions;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
#include "extractor/datasources.hpp"
 | 
			
		||||
#include "extractor/nbg_to_ebg.hpp"
 | 
			
		||||
#include "extractor/node_data_container.hpp"
 | 
			
		||||
#include "extractor/restriction.hpp"
 | 
			
		||||
#include "extractor/segment_data_container.hpp"
 | 
			
		||||
#include "extractor/turn_data_container.hpp"
 | 
			
		||||
 | 
			
		||||
@ -19,6 +20,7 @@ namespace extractor
 | 
			
		||||
namespace serialization
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
// read/write for datasources file
 | 
			
		||||
inline void read(storage::io::FileReader &reader, Datasources &sources)
 | 
			
		||||
{
 | 
			
		||||
    reader.ReadInto(sources);
 | 
			
		||||
@ -29,6 +31,7 @@ inline void write(storage::io::FileWriter &writer, Datasources &sources)
 | 
			
		||||
    writer.WriteFrom(sources);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// read/write for segment data file
 | 
			
		||||
template <storage::Ownership Ownership>
 | 
			
		||||
inline void read(storage::io::FileReader &reader,
 | 
			
		||||
                 detail::SegmentDataContainerImpl<Ownership> &segment_data)
 | 
			
		||||
@ -55,6 +58,7 @@ inline void write(storage::io::FileWriter &writer,
 | 
			
		||||
    storage::serialization::write(writer, segment_data.datasources);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// read/write for turn data file
 | 
			
		||||
template <storage::Ownership Ownership>
 | 
			
		||||
inline void read(storage::io::FileReader &reader,
 | 
			
		||||
                 detail::TurnDataContainerImpl<Ownership> &turn_data_container)
 | 
			
		||||
@ -94,6 +98,51 @@ inline void write(storage::io::FileWriter &writer,
 | 
			
		||||
    storage::serialization::write(writer, node_data_container.name_ids);
 | 
			
		||||
    storage::serialization::write(writer, node_data_container.travel_modes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// read/write for conditional turn restrictions file
 | 
			
		||||
inline void read(storage::io::FileReader &reader, std::vector<TurnRestriction> &restrictions)
 | 
			
		||||
{
 | 
			
		||||
    auto num_indices = reader.ReadElementCount64();
 | 
			
		||||
    restrictions.reserve(num_indices);
 | 
			
		||||
    TurnRestriction restriction;
 | 
			
		||||
    while (num_indices > 0)
 | 
			
		||||
    {
 | 
			
		||||
        bool is_only;
 | 
			
		||||
        reader.ReadInto(restriction.via);
 | 
			
		||||
        reader.ReadInto(restriction.from);
 | 
			
		||||
        reader.ReadInto(restriction.to);
 | 
			
		||||
        reader.ReadInto(is_only);
 | 
			
		||||
        auto num_conditions = reader.ReadElementCount64();
 | 
			
		||||
        restriction.condition.resize(num_conditions);
 | 
			
		||||
        for (uint64_t i = 0; i < num_conditions; i++)
 | 
			
		||||
        {
 | 
			
		||||
            reader.ReadInto(restriction.condition[i].modifier);
 | 
			
		||||
            storage::serialization::read(reader, restriction.condition[i].times);
 | 
			
		||||
            storage::serialization::read(reader, restriction.condition[i].weekdays);
 | 
			
		||||
            storage::serialization::read(reader, restriction.condition[i].monthdays);
 | 
			
		||||
        }
 | 
			
		||||
        restriction.flags.is_only = is_only;
 | 
			
		||||
 | 
			
		||||
        restrictions.push_back(std::move(restriction));
 | 
			
		||||
        num_indices--;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void write(storage::io::FileWriter &writer, const TurnRestriction &restriction)
 | 
			
		||||
{
 | 
			
		||||
    writer.WriteOne(restriction.via);
 | 
			
		||||
    writer.WriteOne(restriction.from);
 | 
			
		||||
    writer.WriteOne(restriction.to);
 | 
			
		||||
    writer.WriteOne(restriction.flags.is_only);
 | 
			
		||||
    writer.WriteElementCount64(restriction.condition.size());
 | 
			
		||||
    for (const auto &c : restriction.condition)
 | 
			
		||||
    {
 | 
			
		||||
        writer.WriteOne(c.modifier);
 | 
			
		||||
        storage::serialization::write(writer, c.times);
 | 
			
		||||
        storage::serialization::write(writer, c.weekdays);
 | 
			
		||||
        storage::serialization::write(writer, c.monthdays);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -63,10 +63,9 @@ struct Turn final
 | 
			
		||||
        : from(from), via(via), to(to)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
    template <typename Other>
 | 
			
		||||
    Turn(const Other &turn)
 | 
			
		||||
        : from(static_cast<std::uint64_t>(turn.from_id)),
 | 
			
		||||
          via(static_cast<std::uint64_t>(turn.via_id)), to(static_cast<std::uint64_t>(turn.to_id))
 | 
			
		||||
    Turn(const OSMNodeID &from_id, const OSMNodeID &via_id, const OSMNodeID &to_id)
 | 
			
		||||
        : from(static_cast<std::uint64_t>(from_id)), via(static_cast<std::uint64_t>(via_id)),
 | 
			
		||||
          to(static_cast<std::uint64_t>(to_id))
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
    bool operator<(const Turn &rhs) const
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
 | 
			
		||||
#include "extractor/edge_based_edge.hpp"
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem/path.hpp>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
@ -53,6 +54,7 @@ struct UpdaterConfig final
 | 
			
		||||
        rtree_leaf_path = osrm_input_path.string() + ".fileIndex";
 | 
			
		||||
        datasource_names_path = osrm_input_path.string() + ".datasource_names";
 | 
			
		||||
        profile_properties_path = osrm_input_path.string() + ".properties";
 | 
			
		||||
        turn_restrictions_path = osrm_input_path.string() + ".restrictions";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boost::filesystem::path osrm_input_path;
 | 
			
		||||
@ -69,11 +71,14 @@ struct UpdaterConfig final
 | 
			
		||||
    std::string rtree_leaf_path;
 | 
			
		||||
 | 
			
		||||
    double log_edge_updates_factor;
 | 
			
		||||
    std::time_t valid_now;
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> segment_speed_lookup_paths;
 | 
			
		||||
    std::vector<std::string> turn_penalty_lookup_paths;
 | 
			
		||||
    std::string datasource_names_path;
 | 
			
		||||
    std::string profile_properties_path;
 | 
			
		||||
    std::string turn_restrictions_path;
 | 
			
		||||
    std::string tz_file_path;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,8 @@
 | 
			
		||||
#ifndef OSRM_CONDITIONAL_RESTRICTIONS_HPP
 | 
			
		||||
#define OSRM_CONDITIONAL_RESTRICTIONS_HPP
 | 
			
		||||
 | 
			
		||||
#include <boost/fusion/include/adapt_struct.hpp>
 | 
			
		||||
#include <boost/spirit/include/phoenix.hpp>
 | 
			
		||||
#include <boost/spirit/include/qi.hpp>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
@ -20,88 +19,7 @@ struct ConditionalRestriction
 | 
			
		||||
    std::string condition;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
// Debug output stream operators for use with BOOST_SPIRIT_DEBUG
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const ConditionalRestriction &restriction)
 | 
			
		||||
{
 | 
			
		||||
    return stream << restriction.value << "=" << restriction.condition;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BOOST_FUSION_ADAPT_STRUCT(osrm::util::ConditionalRestriction,
 | 
			
		||||
                          (std::string, value)(std::string, condition))
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
namespace detail
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
namespace ph = boost::phoenix;
 | 
			
		||||
namespace qi = boost::spirit::qi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Iterator, typename Skipper = qi::blank_type>
 | 
			
		||||
struct conditional_restrictions_grammar
 | 
			
		||||
    : qi::grammar<Iterator, Skipper, std::vector<ConditionalRestriction>()>
 | 
			
		||||
{
 | 
			
		||||
    // http://wiki.openstreetmap.org/wiki/Conditional_restrictions
 | 
			
		||||
    conditional_restrictions_grammar() : conditional_restrictions_grammar::base_type(restrictions)
 | 
			
		||||
    {
 | 
			
		||||
        using qi::_1;
 | 
			
		||||
        using qi::_val;
 | 
			
		||||
        using qi::lit;
 | 
			
		||||
 | 
			
		||||
        // clang-format off
 | 
			
		||||
 | 
			
		||||
        restrictions
 | 
			
		||||
            = restriction % ';'
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        restriction
 | 
			
		||||
            = value >> '@' >> condition
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        value
 | 
			
		||||
            = +(qi::char_ - '@')
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        condition
 | 
			
		||||
            = *qi::blank
 | 
			
		||||
            >> (lit('(') >> qi::as_string[qi::no_skip[*~lit(')')]][_val = _1] >> lit(')')
 | 
			
		||||
                | qi::as_string[qi::no_skip[*~lit(';')]][_val = _1]
 | 
			
		||||
               )
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        // clang-format on
 | 
			
		||||
 | 
			
		||||
        BOOST_SPIRIT_DEBUG_NODES((restrictions)(restriction)(value)(condition));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, std::vector<ConditionalRestriction>()> restrictions;
 | 
			
		||||
    qi::rule<Iterator, Skipper, ConditionalRestriction()> restriction;
 | 
			
		||||
    qi::rule<Iterator, Skipper, std::string()> value, condition;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::vector<ConditionalRestriction> ParseConditionalRestrictions(const std::string &str)
 | 
			
		||||
{
 | 
			
		||||
    auto it(str.begin()), end(str.end());
 | 
			
		||||
    const detail::conditional_restrictions_grammar<decltype(it)> static grammar;
 | 
			
		||||
 | 
			
		||||
    std::vector<ConditionalRestriction> result;
 | 
			
		||||
    bool ok = boost::spirit::qi::phrase_parse(it, end, grammar, boost::spirit::qi::blank, result);
 | 
			
		||||
 | 
			
		||||
    if (!ok || it != end)
 | 
			
		||||
        return std::vector<ConditionalRestriction>();
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
std::vector<ConditionalRestriction> ParseConditionalRestrictions(const std::string &str);
 | 
			
		||||
 | 
			
		||||
} // util
 | 
			
		||||
} // osrm
 | 
			
		||||
 | 
			
		||||
@ -31,24 +31,6 @@ namespace osrm
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reads the .restrictions file and loads it to a vector.
 | 
			
		||||
 * The since the restrictions reference nodes using their external node id,
 | 
			
		||||
 * we need to renumber it to the new internal id.
 | 
			
		||||
*/
 | 
			
		||||
inline unsigned loadRestrictionsFromFile(storage::io::FileReader &file_reader,
 | 
			
		||||
                                         std::vector<extractor::TurnRestriction> &restriction_list)
 | 
			
		||||
{
 | 
			
		||||
    auto number_of_usable_restrictions = file_reader.ReadElementCount64();
 | 
			
		||||
    restriction_list.resize(number_of_usable_restrictions);
 | 
			
		||||
    if (number_of_usable_restrictions > 0)
 | 
			
		||||
    {
 | 
			
		||||
        file_reader.ReadInto(restriction_list.data(), number_of_usable_restrictions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return number_of_usable_restrictions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reads the beginning of an .osrm file and produces:
 | 
			
		||||
 *  - barrier nodes
 | 
			
		||||
 | 
			
		||||
@ -2,17 +2,9 @@
 | 
			
		||||
#define OSRM_OPENING_HOURS_HPP
 | 
			
		||||
 | 
			
		||||
#include <boost/date_time/gregorian/gregorian.hpp>
 | 
			
		||||
#include <boost/spirit/include/phoenix.hpp>
 | 
			
		||||
#include <boost/spirit/include/qi.hpp>
 | 
			
		||||
 | 
			
		||||
#include <boost/io/ios_state.hpp>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cctype>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
@ -219,421 +211,9 @@ struct OpeningHours
 | 
			
		||||
    Modifier modifier;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
// Debug output stream operators for use with BOOST_SPIRIT_DEBUG
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Modifier value)
 | 
			
		||||
{
 | 
			
		||||
    switch (value)
 | 
			
		||||
    {
 | 
			
		||||
    case OpeningHours::unknown:
 | 
			
		||||
        return stream << "unknown";
 | 
			
		||||
    case OpeningHours::open:
 | 
			
		||||
        return stream << "open";
 | 
			
		||||
    case OpeningHours::closed:
 | 
			
		||||
        return stream << "closed";
 | 
			
		||||
    case OpeningHours::off:
 | 
			
		||||
        return stream << "off";
 | 
			
		||||
    case OpeningHours::is24_7:
 | 
			
		||||
        return stream << "24/7";
 | 
			
		||||
    }
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
std::vector<OpeningHours> ParseOpeningHours(const std::string &str);
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Time::Event value)
 | 
			
		||||
{
 | 
			
		||||
    switch (value)
 | 
			
		||||
    {
 | 
			
		||||
    case OpeningHours::Time::dawn:
 | 
			
		||||
        return stream << "dawn";
 | 
			
		||||
    case OpeningHours::Time::sunrise:
 | 
			
		||||
        return stream << "sunrise";
 | 
			
		||||
    case OpeningHours::Time::sunset:
 | 
			
		||||
        return stream << "sunset";
 | 
			
		||||
    case OpeningHours::Time::dusk:
 | 
			
		||||
        return stream << "dusk";
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Time &value)
 | 
			
		||||
{
 | 
			
		||||
    boost::io::ios_flags_saver ifs(stream);
 | 
			
		||||
    if (value.event == OpeningHours::Time::invalid)
 | 
			
		||||
        return stream << "???";
 | 
			
		||||
    if (value.event == OpeningHours::Time::none)
 | 
			
		||||
        return stream << std::setfill('0') << std::setw(2) << value.minutes / 60 << ":"
 | 
			
		||||
                      << std::setfill('0') << std::setw(2) << value.minutes % 60;
 | 
			
		||||
    stream << value.event;
 | 
			
		||||
    if (value.minutes != 0)
 | 
			
		||||
        stream << value.minutes;
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::TimeSpan &value)
 | 
			
		||||
{
 | 
			
		||||
    return stream << value.from << "-" << value.to;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Monthday &value)
 | 
			
		||||
{
 | 
			
		||||
    bool empty = true;
 | 
			
		||||
    if (value.year != 0)
 | 
			
		||||
    {
 | 
			
		||||
        stream << (int)value.year;
 | 
			
		||||
        empty = false;
 | 
			
		||||
    };
 | 
			
		||||
    if (value.month != 0)
 | 
			
		||||
    {
 | 
			
		||||
        stream << (empty ? "" : "/") << (int)value.month;
 | 
			
		||||
        empty = false;
 | 
			
		||||
    };
 | 
			
		||||
    if (value.day != 0)
 | 
			
		||||
    {
 | 
			
		||||
        stream << (empty ? "" : "/") << (int)value.day;
 | 
			
		||||
    };
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::WeekdayRange &value)
 | 
			
		||||
{
 | 
			
		||||
    boost::io::ios_flags_saver ifs(stream);
 | 
			
		||||
    return stream << std::hex << std::setfill('0') << std::setw(2) << value.weekdays;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::MonthdayRange &value)
 | 
			
		||||
{
 | 
			
		||||
    return stream << value.from << "-" << value.to;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours &value)
 | 
			
		||||
{
 | 
			
		||||
    if (value.modifier == OpeningHours::is24_7)
 | 
			
		||||
        return stream << OpeningHours::is24_7;
 | 
			
		||||
 | 
			
		||||
    for (auto x : value.monthdays)
 | 
			
		||||
        stream << x << ", ";
 | 
			
		||||
    for (auto x : value.weekdays)
 | 
			
		||||
        stream << x << ", ";
 | 
			
		||||
    for (auto x : value.times)
 | 
			
		||||
        stream << x << ", ";
 | 
			
		||||
    return stream << " |" << value.modifier << "|";
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace detail
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
namespace ph = boost::phoenix;
 | 
			
		||||
namespace qi = boost::spirit::qi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Iterator, typename Skipper = qi::blank_type>
 | 
			
		||||
struct opening_hours_grammar : qi::grammar<Iterator, Skipper, std::vector<OpeningHours>()>
 | 
			
		||||
{
 | 
			
		||||
    // http://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
 | 
			
		||||
    opening_hours_grammar() : opening_hours_grammar::base_type(time_domain)
 | 
			
		||||
    {
 | 
			
		||||
        using qi::_1;
 | 
			
		||||
        using qi::_a;
 | 
			
		||||
        using qi::_b;
 | 
			
		||||
        using qi::_c;
 | 
			
		||||
        using qi::_r1;
 | 
			
		||||
        using qi::_pass;
 | 
			
		||||
        using qi::_val;
 | 
			
		||||
        using qi::eoi;
 | 
			
		||||
        using qi::lit;
 | 
			
		||||
        using qi::char_;
 | 
			
		||||
        using qi::uint_;
 | 
			
		||||
        using oh = osrm::util::OpeningHours;
 | 
			
		||||
 | 
			
		||||
        // clang-format off
 | 
			
		||||
 | 
			
		||||
        // General syntax
 | 
			
		||||
        time_domain = rule_sequence[ph::push_back(_val, _1)] % any_rule_separator;
 | 
			
		||||
 | 
			
		||||
        rule_sequence
 | 
			
		||||
            = lit("24/7")[ph::bind(&oh::modifier, _val) = oh::is24_7]
 | 
			
		||||
            | (selector_sequence[_val = _1] >> -rule_modifier[ph::bind(&oh::modifier, _val) = _1] >> -comment)
 | 
			
		||||
            | comment
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        any_rule_separator = char_(';') | lit("||") | additional_rule_separator;
 | 
			
		||||
 | 
			
		||||
        additional_rule_separator = char_(',');
 | 
			
		||||
 | 
			
		||||
        // Rule modifiers
 | 
			
		||||
        rule_modifier.add
 | 
			
		||||
            ("unknown", oh::unknown)
 | 
			
		||||
            ("open", oh::open)
 | 
			
		||||
            ("closed", oh::closed)
 | 
			
		||||
            ("off", oh::off)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        // Selectors
 | 
			
		||||
        selector_sequence = (wide_range_selectors(_a) >> small_range_selectors(_a))[_val = _a];
 | 
			
		||||
 | 
			
		||||
        wide_range_selectors
 | 
			
		||||
            = (-monthday_selector(_r1)
 | 
			
		||||
               >> -year_selector(_r1)
 | 
			
		||||
               >> -week_selector(_r1) // TODO week_selector
 | 
			
		||||
              ) >> -lit(':')
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        small_range_selectors = -(weekday_selector(_r1) >> (&~lit(',') | eoi)) >> -time_selector(_r1);
 | 
			
		||||
 | 
			
		||||
        // Time selector
 | 
			
		||||
        time_selector = (timespan % ',')[ph::bind(&OpeningHours::times, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        timespan
 | 
			
		||||
            = (time[_a = _1]
 | 
			
		||||
               >> -(lit('+')[_b = ph::construct<OpeningHours::Time>(24, 0)]
 | 
			
		||||
                    | ('-' >> extended_time[_b = _1]
 | 
			
		||||
                       >> -('+' | '/' >> (minute | hour_minutes))))
 | 
			
		||||
               )[_val = ph::construct<OpeningHours::TimeSpan>(_a, _b)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        time = hour_minutes | variable_time;
 | 
			
		||||
 | 
			
		||||
        extended_time = extended_hour_minutes | variable_time;
 | 
			
		||||
 | 
			
		||||
        variable_time
 | 
			
		||||
            = event[_val = ph::construct<OpeningHours::Time>(_1)]
 | 
			
		||||
            | ('(' >> event[_a = _1] >> plus_or_minus[_b = _1] >> hour_minutes[_c = _1] >> ')')
 | 
			
		||||
            [_val = ph::construct<OpeningHours::Time>(_a, _b, _c)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        event.add
 | 
			
		||||
            ("dawn", OpeningHours::Time::dawn)
 | 
			
		||||
            ("sunrise", OpeningHours::Time::sunrise)
 | 
			
		||||
            ("sunset", OpeningHours::Time::sunset)
 | 
			
		||||
            ("dusk", OpeningHours::Time::dusk)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        // Weekday selector
 | 
			
		||||
        weekday_selector
 | 
			
		||||
            = (holiday_sequence(_r1) >> -(char_(", ") >> weekday_sequence(_r1)))
 | 
			
		||||
            | (weekday_sequence(_r1) >> -(char_(", ") >> holiday_sequence(_r1)))
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        weekday_sequence = (weekday_range % ',')[ph::bind(&OpeningHours::weekdays, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        weekday_range
 | 
			
		||||
            = wday[_a = _1, _b = _1]
 | 
			
		||||
            >> -(('-' >> wday[_b = _1])
 | 
			
		||||
                 | ('[' >> (nth_entry % ',') >> ']' >> -day_offset))
 | 
			
		||||
            [_val = ph::construct<OpeningHours::WeekdayRange>(_a, _b)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        holiday_sequence = (lit("SH") >> -day_offset) | lit("PH");
 | 
			
		||||
 | 
			
		||||
        nth_entry = nth | nth >> '-' >> nth | '-' >> nth;
 | 
			
		||||
 | 
			
		||||
        nth = char_("12345");
 | 
			
		||||
 | 
			
		||||
        day_offset = plus_or_minus >> uint_ >> lit("days");
 | 
			
		||||
 | 
			
		||||
        // Week selector
 | 
			
		||||
        week_selector = (lit("week ") >> week) % ',';
 | 
			
		||||
 | 
			
		||||
        week = weeknum >> -('-' >> weeknum >> -('/' >> uint_));
 | 
			
		||||
 | 
			
		||||
        // Month selector
 | 
			
		||||
        monthday_selector = (monthday_range % ',')[ph::bind(&OpeningHours::monthdays, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        monthday_range
 | 
			
		||||
            = (date_from[ph::bind(&OpeningHours::MonthdayRange::from, _val) = _1]
 | 
			
		||||
               >> -date_offset
 | 
			
		||||
               >> '-'
 | 
			
		||||
               >> date_to[ph::bind(&OpeningHours::MonthdayRange::to, _val) = _1]
 | 
			
		||||
               >> -date_offset)
 | 
			
		||||
            | (date_from[ph::bind(&OpeningHours::MonthdayRange::from, _val) = _1]
 | 
			
		||||
               >> -(date_offset
 | 
			
		||||
                    >> -lit('+')[ph::bind(&OpeningHours::MonthdayRange::from, _val) = ph::construct<OpeningHours::Monthday>(-1)]
 | 
			
		||||
                    ))
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        date_offset = (plus_or_minus >> wday) | day_offset;
 | 
			
		||||
 | 
			
		||||
        date_from
 | 
			
		||||
            = ((-year[_a = _1] >> ((month[_b = _1] >> -daynum[_c = _1]) | daynum[_c = _1]))
 | 
			
		||||
               | variable_date)
 | 
			
		||||
            [_val = ph::construct<OpeningHours::Monthday>(_a, _b, _c)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        date_to
 | 
			
		||||
            = date_from[_val = _1]
 | 
			
		||||
            | daynum[_val = ph::construct<OpeningHours::Monthday>(0, 0, _1)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        variable_date = lit("easter");
 | 
			
		||||
 | 
			
		||||
        // Year selector
 | 
			
		||||
        year_selector = (year_range % ',')[ph::bind(&OpeningHours::monthdays, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        year_range
 | 
			
		||||
            = year[ph::bind(&OpeningHours::MonthdayRange::from, _val) = ph::construct<OpeningHours::Monthday>(_1)]
 | 
			
		||||
            >> -(('-' >> year[ph::bind(&OpeningHours::MonthdayRange::to, _val) = ph::construct<OpeningHours::Monthday>(_1)]
 | 
			
		||||
                  >> -('/' >> uint_))
 | 
			
		||||
                 | lit('+')[ph::bind(&OpeningHours::MonthdayRange::to, _val) = ph::construct<OpeningHours::Monthday>(-1)]);
 | 
			
		||||
 | 
			
		||||
        // Basic elements
 | 
			
		||||
        plus_or_minus = lit('+')[_val = true] | lit('-')[_val = false];
 | 
			
		||||
 | 
			
		||||
        hour = uint2_p[_pass = bind([](unsigned x) { return x <= 24; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        extended_hour = uint2_p[_pass = bind([](unsigned x) { return x <= 48; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        minute = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        hour_minutes =
 | 
			
		||||
            hour[_a = _1] >> ':' >> minute[_val = ph::construct<OpeningHours::Time>(_a, _1)];
 | 
			
		||||
 | 
			
		||||
        extended_hour_minutes = extended_hour[_a = _1] >> ':' >>
 | 
			
		||||
                                minute[_val = ph::construct<OpeningHours::Time>(_a, _1)];
 | 
			
		||||
 | 
			
		||||
        wday.add
 | 
			
		||||
            ("Su", 0)
 | 
			
		||||
            ("Mo", 1)
 | 
			
		||||
            ("Tu", 2)
 | 
			
		||||
            ("We", 3)
 | 
			
		||||
            ("Th", 4)
 | 
			
		||||
            ("Fr", 5)
 | 
			
		||||
            ("Sa", 6)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        daynum
 | 
			
		||||
            = uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 31; }, _1), _val = _1]
 | 
			
		||||
            >> (&~lit(':') | eoi)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        weeknum = uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 53; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        month.add
 | 
			
		||||
            ("Jan", 1)
 | 
			
		||||
            ("Feb", 2)
 | 
			
		||||
            ("Mar", 3)
 | 
			
		||||
            ("Apr", 4)
 | 
			
		||||
            ("May", 5)
 | 
			
		||||
            ("Jun", 6)
 | 
			
		||||
            ("Jul", 7)
 | 
			
		||||
            ("Aug", 8)
 | 
			
		||||
            ("Sep", 9)
 | 
			
		||||
            ("Oct", 10)
 | 
			
		||||
            ("Nov", 11)
 | 
			
		||||
            ("Dec", 12)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        year = uint4_p[_pass = bind([](unsigned x) { return x > 1900; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        comment = lit('"') >> *(~qi::char_('"')) >> lit('"');
 | 
			
		||||
 | 
			
		||||
        // clang-format on
 | 
			
		||||
 | 
			
		||||
        BOOST_SPIRIT_DEBUG_NODES((time_domain)(rule_sequence)(any_rule_separator)(
 | 
			
		||||
            selector_sequence)(wide_range_selectors)(small_range_selectors)(time_selector)(
 | 
			
		||||
            timespan)(time)(extended_time)(variable_time)(weekday_selector)(weekday_sequence)(
 | 
			
		||||
            weekday_range)(holiday_sequence)(nth_entry)(nth)(day_offset)(week_selector)(week)(
 | 
			
		||||
            monthday_selector)(monthday_range)(date_offset)(date_from)(date_to)(variable_date)(
 | 
			
		||||
            year_selector)(year_range)(plus_or_minus)(hour_minutes)(extended_hour_minutes)(comment)(
 | 
			
		||||
            hour)(extended_hour)(minute)(daynum)(weeknum)(year));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, std::vector<OpeningHours>()> time_domain;
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours()> rule_sequence;
 | 
			
		||||
    qi::rule<Iterator, Skipper, void()> any_rule_separator, additional_rule_separator;
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours(), qi::locals<OpeningHours>> selector_sequence;
 | 
			
		||||
    qi::symbols<char const, OpeningHours::Modifier> rule_modifier;
 | 
			
		||||
    qi::rule<Iterator, Skipper, void(OpeningHours &)> wide_range_selectors, small_range_selectors,
 | 
			
		||||
        time_selector, weekday_selector, year_selector, monthday_selector, week_selector;
 | 
			
		||||
 | 
			
		||||
    // Time rules
 | 
			
		||||
    qi::rule<Iterator,
 | 
			
		||||
             Skipper,
 | 
			
		||||
             OpeningHours::TimeSpan(),
 | 
			
		||||
             qi::locals<OpeningHours::Time, OpeningHours::Time>>
 | 
			
		||||
        timespan;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Time()> time, extended_time;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator,
 | 
			
		||||
             Skipper,
 | 
			
		||||
             OpeningHours::Time(),
 | 
			
		||||
             qi::locals<OpeningHours::Time::Event, bool, OpeningHours::Time>>
 | 
			
		||||
        variable_time;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Time(), qi::locals<unsigned>> hour_minutes,
 | 
			
		||||
        extended_hour_minutes;
 | 
			
		||||
 | 
			
		||||
    qi::symbols<char const, OpeningHours::Time::Event> event;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, bool()> plus_or_minus;
 | 
			
		||||
 | 
			
		||||
    // Weekday rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, void(OpeningHours &)> weekday_sequence, holiday_sequence;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator,
 | 
			
		||||
             Skipper,
 | 
			
		||||
             OpeningHours::WeekdayRange(),
 | 
			
		||||
             qi::locals<unsigned char, unsigned char>>
 | 
			
		||||
        weekday_range;
 | 
			
		||||
 | 
			
		||||
    // Monthday rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::MonthdayRange()> monthday_range;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Monthday(), qi::locals<unsigned, unsigned, unsigned>>
 | 
			
		||||
        date_from;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Monthday()> date_to;
 | 
			
		||||
 | 
			
		||||
    // Year rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::MonthdayRange()> year_range;
 | 
			
		||||
 | 
			
		||||
    // Unused rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, void()> nth_entry, nth, day_offset, week, date_offset,
 | 
			
		||||
        variable_date, comment;
 | 
			
		||||
 | 
			
		||||
    // Basic rules and parsers
 | 
			
		||||
    qi::rule<Iterator, Skipper, unsigned()> hour, extended_hour, minute, daynum, weeknum, year;
 | 
			
		||||
    qi::symbols<char const, unsigned char> wday, month;
 | 
			
		||||
    qi::uint_parser<unsigned, 10, 2, 2> uint2_p;
 | 
			
		||||
    qi::uint_parser<unsigned, 10, 4, 4> uint4_p;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::vector<OpeningHours> ParseOpeningHours(const std::string &str)
 | 
			
		||||
{
 | 
			
		||||
    auto it(str.begin()), end(str.end());
 | 
			
		||||
    const detail::opening_hours_grammar<decltype(it)> static grammar;
 | 
			
		||||
 | 
			
		||||
    std::vector<OpeningHours> result;
 | 
			
		||||
    bool ok = boost::spirit::qi::phrase_parse(it, end, grammar, boost::spirit::qi::blank, result);
 | 
			
		||||
 | 
			
		||||
    if (!ok || it != end)
 | 
			
		||||
        return std::vector<OpeningHours>();
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool CheckOpeningHours(const std::vector<OpeningHours> &input, const struct tm &time)
 | 
			
		||||
{
 | 
			
		||||
    bool is_open = false;
 | 
			
		||||
    for (auto &opening_hours : input)
 | 
			
		||||
    {
 | 
			
		||||
        if (opening_hours.modifier == OpeningHours::is24_7)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (opening_hours.IsInRange(time))
 | 
			
		||||
        {
 | 
			
		||||
            is_open = opening_hours.modifier == OpeningHours::open;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return is_open;
 | 
			
		||||
}
 | 
			
		||||
bool CheckOpeningHours(const std::vector<OpeningHours> &input, const struct tm &time);
 | 
			
		||||
 | 
			
		||||
} // util
 | 
			
		||||
} // osrm
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								include/util/timezones.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								include/util/timezones.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
#ifndef OSRM_TIMEZONES_HPP
 | 
			
		||||
#define OSRM_TIMEZONES_HPP
 | 
			
		||||
 | 
			
		||||
#include "util/log.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/geometry.hpp>
 | 
			
		||||
#include <boost/geometry/index/rtree.hpp>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace updater
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
// Time zone shape polygons loaded in R-tree
 | 
			
		||||
// local_time_t is a pair of a time zone shape polygon and the corresponding local time
 | 
			
		||||
// rtree_t is a lookup R-tree that maps a geographic point to an index in a local_time_t vector
 | 
			
		||||
using point_t = boost::geometry::model::
 | 
			
		||||
    point<double, 2, boost::geometry::cs::spherical_equatorial<boost::geometry::degree>>;
 | 
			
		||||
using polygon_t = boost::geometry::model::polygon<point_t>;
 | 
			
		||||
using box_t = boost::geometry::model::box<point_t>;
 | 
			
		||||
using rtree_t =
 | 
			
		||||
    boost::geometry::index::rtree<std::pair<box_t, size_t>, boost::geometry::index::rstar<8>>;
 | 
			
		||||
using local_time_t = std::pair<polygon_t, struct tm>;
 | 
			
		||||
 | 
			
		||||
bool SupportsShapefiles();
 | 
			
		||||
 | 
			
		||||
class Timezoner
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
    Timezoner() = default;
 | 
			
		||||
 | 
			
		||||
    Timezoner(std::string tz_filename, std::time_t utc_time_now);
 | 
			
		||||
 | 
			
		||||
    struct tm operator()(const point_t &point) const;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    void LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time);
 | 
			
		||||
 | 
			
		||||
    struct tm default_time;
 | 
			
		||||
    rtree_t rtree;
 | 
			
		||||
    std::vector<local_time_t> local_times;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@ -17,6 +17,7 @@
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "lint": "eslint -c ./.eslintrc features/step_definitions/ features/support/",
 | 
			
		||||
    "test": "npm run lint && node ./node_modules/cucumber/bin/cucumber.js features/ -p verify && node ./node_modules/cucumber/bin/cucumber.js features/ -p mld",
 | 
			
		||||
    "test-conditionals": "npm run lint && node ./node_modules/cucumber/bin/cucumber.js features/ -p conditionals && node ./node_modules/cucumber/bin/cucumber.js features/ -p mld_conditionals",
 | 
			
		||||
    "clean": "rm -rf test/cache",
 | 
			
		||||
    "docs": "./scripts/build_api_docs.sh",
 | 
			
		||||
    "install": "node-pre-gyp install --fallback-to-build=false || ./scripts/node_install.sh",
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,8 @@ target_include_directories(rtree-bench
 | 
			
		||||
target_link_libraries(rtree-bench
 | 
			
		||||
	${BOOST_BASE_LIBRARIES}
 | 
			
		||||
	${CMAKE_THREAD_LIBS_INIT}
 | 
			
		||||
	${TBB_LIBRARIES})
 | 
			
		||||
	${TBB_LIBRARIES}
 | 
			
		||||
	${MAYBE_SHAPEFILE})
 | 
			
		||||
 | 
			
		||||
add_executable(match-bench
 | 
			
		||||
	EXCLUDE_FROM_ALL
 | 
			
		||||
@ -25,7 +26,8 @@ target_link_libraries(match-bench
 | 
			
		||||
	osrm
 | 
			
		||||
	${BOOST_BASE_LIBRARIES}
 | 
			
		||||
	${CMAKE_THREAD_LIBS_INIT}
 | 
			
		||||
	${TBB_LIBRARIES})
 | 
			
		||||
	${TBB_LIBRARIES}
 | 
			
		||||
	${MAYBE_SHAPEFILE})
 | 
			
		||||
 | 
			
		||||
add_executable(alias-bench
 | 
			
		||||
	EXCLUDE_FROM_ALL
 | 
			
		||||
@ -35,7 +37,8 @@ add_executable(alias-bench
 | 
			
		||||
target_link_libraries(alias-bench
 | 
			
		||||
	${BOOST_BASE_LIBRARIES}
 | 
			
		||||
	${CMAKE_THREAD_LIBS_INIT}
 | 
			
		||||
	${TBB_LIBRARIES})
 | 
			
		||||
	${TBB_LIBRARIES}
 | 
			
		||||
	${MAYBE_SHAPEFILE})
 | 
			
		||||
 | 
			
		||||
add_custom_target(benchmarks
 | 
			
		||||
	DEPENDS
 | 
			
		||||
 | 
			
		||||
@ -547,20 +547,17 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
 | 
			
		||||
                    // If this edge is 'trivial' -- where the compressed edge corresponds
 | 
			
		||||
                    // exactly to an original OSM segment -- we can pull the turn's preceding
 | 
			
		||||
                    // node ID directly with `node_along_road_entering`; otherwise, we need to
 | 
			
		||||
                    // look
 | 
			
		||||
                    // up the node
 | 
			
		||||
                    // immediately preceding the turn from the compressed edge container.
 | 
			
		||||
                    // look up the node immediately preceding the turn from the compressed edge
 | 
			
		||||
                    // container.
 | 
			
		||||
                    const bool isTrivial = m_compressed_edge_container.IsTrivial(incoming_edge);
 | 
			
		||||
 | 
			
		||||
                    const auto &from_node =
 | 
			
		||||
                        isTrivial ? m_osm_node_ids[node_along_road_entering]
 | 
			
		||||
                                  : m_osm_node_ids[m_compressed_edge_container.GetLastEdgeSourceID(
 | 
			
		||||
                                        incoming_edge)];
 | 
			
		||||
                        isTrivial ? node_along_road_entering
 | 
			
		||||
                                  : m_compressed_edge_container.GetLastEdgeSourceID(incoming_edge);
 | 
			
		||||
                    const auto &via_node =
 | 
			
		||||
                        m_osm_node_ids[m_compressed_edge_container.GetLastEdgeTargetID(
 | 
			
		||||
                            incoming_edge)];
 | 
			
		||||
                        m_compressed_edge_container.GetLastEdgeTargetID(incoming_edge);
 | 
			
		||||
                    const auto &to_node =
 | 
			
		||||
                        m_osm_node_ids[m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid)];
 | 
			
		||||
                        m_compressed_edge_container.GetFirstEdgeTargetID(turn.eid);
 | 
			
		||||
 | 
			
		||||
                    lookup::TurnIndexBlock turn_index_block = {from_node, via_node, to_node};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
#include "extractor/extraction_containers.hpp"
 | 
			
		||||
#include "extractor/extraction_segment.hpp"
 | 
			
		||||
#include "extractor/extraction_way.hpp"
 | 
			
		||||
#include "extractor/restriction.hpp"
 | 
			
		||||
#include "extractor/serialization.hpp"
 | 
			
		||||
 | 
			
		||||
#include "util/coordinate_calculation.hpp"
 | 
			
		||||
 | 
			
		||||
@ -134,7 +136,6 @@ void ExtractionContainers::FlushVectors()
 | 
			
		||||
    all_edges_list.flush();
 | 
			
		||||
    name_char_data.flush();
 | 
			
		||||
    name_offsets.flush();
 | 
			
		||||
    restrictions_list.flush();
 | 
			
		||||
    way_start_end_id_list.flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -142,7 +143,7 @@ void ExtractionContainers::FlushVectors()
 | 
			
		||||
 * Processes the collected data and serializes it.
 | 
			
		||||
 * At this point nodes are still referenced by their OSM id.
 | 
			
		||||
 *
 | 
			
		||||
 * - map start-end nodes of ways to ways used int restrictions to compute compressed
 | 
			
		||||
 * - map start-end nodes of ways to ways used in restrictions to compute compressed
 | 
			
		||||
 *   trippe representation
 | 
			
		||||
 * - filter nodes list to nodes that are referenced by ways
 | 
			
		||||
 * - merge edges with nodes to include location of start/end points and serialize
 | 
			
		||||
@ -630,7 +631,7 @@ void ExtractionContainers::WriteNodes(storage::io::FileWriter &file_out) const
 | 
			
		||||
    util::Log() << "Processed " << max_internal_node_id << " nodes";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ExtractionContainers::WriteRestrictions(const std::string &path) const
 | 
			
		||||
void ExtractionContainers::WriteRestrictions(const std::string &path)
 | 
			
		||||
{
 | 
			
		||||
    // serialize restrictions
 | 
			
		||||
    std::uint64_t written_restriction_count = 0;
 | 
			
		||||
@ -645,13 +646,27 @@ void ExtractionContainers::WriteRestrictions(const std::string &path) const
 | 
			
		||||
            SPECIAL_NODEID != restriction_container.restriction.via.node &&
 | 
			
		||||
            SPECIAL_NODEID != restriction_container.restriction.to.node)
 | 
			
		||||
        {
 | 
			
		||||
            restrictions_out_file.WriteOne(restriction_container.restriction);
 | 
			
		||||
            ++written_restriction_count;
 | 
			
		||||
            if (!restriction_container.restriction.condition.empty())
 | 
			
		||||
            {
 | 
			
		||||
                // write conditional turn restrictions to disk, for use in contractor later
 | 
			
		||||
                extractor::serialization::write(restrictions_out_file,
 | 
			
		||||
                                                restriction_container.restriction);
 | 
			
		||||
                ++written_restriction_count;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // save unconditional turn restriction to memory, for use in ebg later
 | 
			
		||||
                unconditional_turn_restrictions.push_back(
 | 
			
		||||
                    std::move(restriction_container.restriction));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    restrictions_out_file.SkipToBeginning();
 | 
			
		||||
    restrictions_out_file.WriteElementCount64(written_restriction_count);
 | 
			
		||||
    util::Log() << "usable restrictions: " << written_restriction_count;
 | 
			
		||||
    util::Log() << "number of restrictions saved to memory: "
 | 
			
		||||
                << unconditional_turn_restrictions.size();
 | 
			
		||||
    util::Log() << "number of conditional restrictions written to disk: "
 | 
			
		||||
                << written_restriction_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ExtractionContainers::PrepareRestrictions()
 | 
			
		||||
@ -672,10 +687,8 @@ void ExtractionContainers::PrepareRestrictions()
 | 
			
		||||
        util::UnbufferedLog log;
 | 
			
		||||
        log << "Sorting " << restrictions_list.size() << " restriction. by from... ";
 | 
			
		||||
        TIMER_START(sort_restrictions);
 | 
			
		||||
        stxxl::sort(restrictions_list.begin(),
 | 
			
		||||
                    restrictions_list.end(),
 | 
			
		||||
                    CmpRestrictionContainerByFrom(),
 | 
			
		||||
                    stxxl_memory);
 | 
			
		||||
        std::sort(
 | 
			
		||||
            restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByFrom());
 | 
			
		||||
        TIMER_STOP(sort_restrictions);
 | 
			
		||||
        log << "ok, after " << TIMER_SEC(sort_restrictions) << "s";
 | 
			
		||||
    }
 | 
			
		||||
@ -758,6 +771,11 @@ void ExtractionContainers::PrepareRestrictions()
 | 
			
		||||
                }
 | 
			
		||||
                restrictions_iterator->restriction.from.node = id_iter->second;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // if it's neither, this is an invalid restriction
 | 
			
		||||
                restrictions_iterator->restriction.from.node = SPECIAL_NODEID;
 | 
			
		||||
            }
 | 
			
		||||
            ++restrictions_iterator;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -769,10 +787,8 @@ void ExtractionContainers::PrepareRestrictions()
 | 
			
		||||
        util::UnbufferedLog log;
 | 
			
		||||
        log << "Sorting restrictions. by to  ... " << std::flush;
 | 
			
		||||
        TIMER_START(sort_restrictions_to);
 | 
			
		||||
        stxxl::sort(restrictions_list.begin(),
 | 
			
		||||
                    restrictions_list.end(),
 | 
			
		||||
                    CmpRestrictionContainerByTo(),
 | 
			
		||||
                    stxxl_memory);
 | 
			
		||||
        std::sort(
 | 
			
		||||
            restrictions_list.begin(), restrictions_list.end(), CmpRestrictionContainerByTo());
 | 
			
		||||
        TIMER_STOP(sort_restrictions_to);
 | 
			
		||||
        log << "ok, after " << TIMER_SEC(sort_restrictions_to) << "s";
 | 
			
		||||
    }
 | 
			
		||||
@ -850,6 +866,11 @@ void ExtractionContainers::PrepareRestrictions()
 | 
			
		||||
                }
 | 
			
		||||
                restrictions_iterator->restriction.to.node = to_id_iter->second;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // if it's neither, this is an invalid restriction
 | 
			
		||||
                restrictions_iterator->restriction.to.node = SPECIAL_NODEID;
 | 
			
		||||
            }
 | 
			
		||||
            ++restrictions_iterator;
 | 
			
		||||
        }
 | 
			
		||||
        TIMER_STOP(fix_restriction_ends);
 | 
			
		||||
 | 
			
		||||
@ -115,208 +115,216 @@ transformTurnLaneMapIntoArrays(const guidance::LaneDescriptionMap &turn_lane_map
 | 
			
		||||
int Extractor::run(ScriptingEnvironment &scripting_environment)
 | 
			
		||||
{
 | 
			
		||||
    util::LogPolicy::GetInstance().Unmute();
 | 
			
		||||
    TIMER_START(extracting);
 | 
			
		||||
 | 
			
		||||
    const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads();
 | 
			
		||||
    const auto number_of_threads = std::min(recommended_num_threads, config.requested_num_threads);
 | 
			
		||||
    tbb::task_scheduler_init init(number_of_threads ? number_of_threads
 | 
			
		||||
                                                    : tbb::task_scheduler_init::automatic);
 | 
			
		||||
 | 
			
		||||
    auto turn_restrictions = ParseOSMData(scripting_environment, number_of_threads);
 | 
			
		||||
 | 
			
		||||
    // Transform the node-based graph that OSM is based on into an edge-based graph
 | 
			
		||||
    // that is better for routing.  Every edge becomes a node, and every valid
 | 
			
		||||
    // movement (e.g. turn from A->B, and B->A) becomes an edge
 | 
			
		||||
    util::Log() << "Generating edge-expanded graph representation";
 | 
			
		||||
 | 
			
		||||
    TIMER_START(expansion);
 | 
			
		||||
 | 
			
		||||
    std::vector<EdgeBasedNode> edge_based_node_list;
 | 
			
		||||
    util::DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
 | 
			
		||||
    std::vector<bool> node_is_startpoint;
 | 
			
		||||
    std::vector<EdgeWeight> edge_based_node_weights;
 | 
			
		||||
    std::vector<util::Coordinate> coordinates;
 | 
			
		||||
    extractor::PackedOSMIDs osm_node_ids;
 | 
			
		||||
 | 
			
		||||
    auto graph_size = BuildEdgeExpandedGraph(scripting_environment,
 | 
			
		||||
                                             coordinates,
 | 
			
		||||
                                             osm_node_ids,
 | 
			
		||||
                                             edge_based_node_list,
 | 
			
		||||
                                             node_is_startpoint,
 | 
			
		||||
                                             edge_based_node_weights,
 | 
			
		||||
                                             edge_based_edge_list,
 | 
			
		||||
                                             config.intersection_class_data_output_path,
 | 
			
		||||
                                             turn_restrictions);
 | 
			
		||||
 | 
			
		||||
    auto number_of_node_based_nodes = graph_size.first;
 | 
			
		||||
    auto max_edge_id = graph_size.second;
 | 
			
		||||
 | 
			
		||||
    TIMER_STOP(expansion);
 | 
			
		||||
 | 
			
		||||
    util::Log() << "Saving edge-based node weights to file.";
 | 
			
		||||
    TIMER_START(timer_write_node_weights);
 | 
			
		||||
    {
 | 
			
		||||
        util::Log() << "Input file: " << config.input_path.filename().string();
 | 
			
		||||
        if (!config.profile_path.empty())
 | 
			
		||||
        {
 | 
			
		||||
            util::Log() << "Profile: " << config.profile_path.filename().string();
 | 
			
		||||
        }
 | 
			
		||||
        util::Log() << "Threads: " << number_of_threads;
 | 
			
		||||
 | 
			
		||||
        const osmium::io::File input_file(config.input_path.string());
 | 
			
		||||
 | 
			
		||||
        osmium::io::Reader reader(
 | 
			
		||||
            input_file,
 | 
			
		||||
            (config.use_metadata ? osmium::io::read_meta::yes : osmium::io::read_meta::no));
 | 
			
		||||
 | 
			
		||||
        const osmium::io::Header header = reader.header();
 | 
			
		||||
 | 
			
		||||
        unsigned number_of_nodes = 0;
 | 
			
		||||
        unsigned number_of_ways = 0;
 | 
			
		||||
        unsigned number_of_relations = 0;
 | 
			
		||||
 | 
			
		||||
        util::Log() << "Parsing in progress..";
 | 
			
		||||
        TIMER_START(parsing);
 | 
			
		||||
 | 
			
		||||
        ExtractionContainers extraction_containers;
 | 
			
		||||
        auto extractor_callbacks = std::make_unique<ExtractorCallbacks>(
 | 
			
		||||
            extraction_containers, scripting_environment.GetProfileProperties());
 | 
			
		||||
 | 
			
		||||
        // setup raster sources
 | 
			
		||||
        scripting_environment.SetupSources();
 | 
			
		||||
 | 
			
		||||
        std::string generator = header.get("generator");
 | 
			
		||||
        if (generator.empty())
 | 
			
		||||
        {
 | 
			
		||||
            generator = "unknown tool";
 | 
			
		||||
        }
 | 
			
		||||
        util::Log() << "input file generated by " << generator;
 | 
			
		||||
 | 
			
		||||
        // write .timestamp data file
 | 
			
		||||
        std::string timestamp = header.get("osmosis_replication_timestamp");
 | 
			
		||||
        if (timestamp.empty())
 | 
			
		||||
        {
 | 
			
		||||
            timestamp = "n/a";
 | 
			
		||||
        }
 | 
			
		||||
        util::Log() << "timestamp: " << timestamp;
 | 
			
		||||
 | 
			
		||||
        storage::io::FileWriter timestamp_file(config.timestamp_file_name,
 | 
			
		||||
                                               storage::io::FileWriter::GenerateFingerprint);
 | 
			
		||||
 | 
			
		||||
        timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length());
 | 
			
		||||
 | 
			
		||||
        // initialize vectors holding parsed objects
 | 
			
		||||
        tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
 | 
			
		||||
        tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
 | 
			
		||||
        tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
 | 
			
		||||
 | 
			
		||||
        // setup restriction parser
 | 
			
		||||
        const RestrictionParser restriction_parser(scripting_environment);
 | 
			
		||||
 | 
			
		||||
        // create a vector of iterators into the buffer
 | 
			
		||||
        for (std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
 | 
			
		||||
             const osmium::memory::Buffer buffer = reader.read();
 | 
			
		||||
             osm_elements.clear())
 | 
			
		||||
        {
 | 
			
		||||
            for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
 | 
			
		||||
            {
 | 
			
		||||
                osm_elements.push_back(iter);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // clear resulting vectors
 | 
			
		||||
            resulting_nodes.clear();
 | 
			
		||||
            resulting_ways.clear();
 | 
			
		||||
            resulting_restrictions.clear();
 | 
			
		||||
 | 
			
		||||
            scripting_environment.ProcessElements(osm_elements,
 | 
			
		||||
                                                  restriction_parser,
 | 
			
		||||
                                                  resulting_nodes,
 | 
			
		||||
                                                  resulting_ways,
 | 
			
		||||
                                                  resulting_restrictions);
 | 
			
		||||
 | 
			
		||||
            number_of_nodes += resulting_nodes.size();
 | 
			
		||||
            // put parsed objects thru extractor callbacks
 | 
			
		||||
            for (const auto &result : resulting_nodes)
 | 
			
		||||
            {
 | 
			
		||||
                extractor_callbacks->ProcessNode(
 | 
			
		||||
                    static_cast<const osmium::Node &>(*(osm_elements[result.first])),
 | 
			
		||||
                    result.second);
 | 
			
		||||
            }
 | 
			
		||||
            number_of_ways += resulting_ways.size();
 | 
			
		||||
            for (const auto &result : resulting_ways)
 | 
			
		||||
            {
 | 
			
		||||
                extractor_callbacks->ProcessWay(
 | 
			
		||||
                    static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
 | 
			
		||||
            }
 | 
			
		||||
            number_of_relations += resulting_restrictions.size();
 | 
			
		||||
            for (const auto &result : resulting_restrictions)
 | 
			
		||||
            {
 | 
			
		||||
                extractor_callbacks->ProcessRestriction(result);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        TIMER_STOP(parsing);
 | 
			
		||||
        util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
 | 
			
		||||
 | 
			
		||||
        util::Log() << "Raw input contains " << number_of_nodes << " nodes, " << number_of_ways
 | 
			
		||||
                    << " ways, and " << number_of_relations << " relations";
 | 
			
		||||
 | 
			
		||||
        // take control over the turn lane map
 | 
			
		||||
        turn_lane_map = extractor_callbacks->moveOutLaneDescriptionMap();
 | 
			
		||||
 | 
			
		||||
        extractor_callbacks.reset();
 | 
			
		||||
 | 
			
		||||
        if (extraction_containers.all_edges_list.empty())
 | 
			
		||||
        {
 | 
			
		||||
            throw util::exception(std::string("There are no edges remaining after parsing.") +
 | 
			
		||||
                                  SOURCE_REF);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        extraction_containers.PrepareData(scripting_environment,
 | 
			
		||||
                                          config.output_file_name,
 | 
			
		||||
                                          config.restriction_file_name,
 | 
			
		||||
                                          config.names_file_name);
 | 
			
		||||
 | 
			
		||||
        WriteProfileProperties(config.profile_properties_output_path,
 | 
			
		||||
                               scripting_environment.GetProfileProperties());
 | 
			
		||||
 | 
			
		||||
        TIMER_STOP(extracting);
 | 
			
		||||
        util::Log() << "extraction finished after " << TIMER_SEC(extracting) << "s";
 | 
			
		||||
        storage::io::FileWriter writer(config.edge_based_node_weights_output_path,
 | 
			
		||||
                                       storage::io::FileWriter::GenerateFingerprint);
 | 
			
		||||
        storage::serialization::write(writer, edge_based_node_weights);
 | 
			
		||||
    }
 | 
			
		||||
    TIMER_STOP(timer_write_node_weights);
 | 
			
		||||
    util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")";
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        // Transform the node-based graph that OSM is based on into an edge-based graph
 | 
			
		||||
        // that is better for routing.  Every edge becomes a node, and every valid
 | 
			
		||||
        // movement (e.g. turn from A->B, and B->A) becomes an edge
 | 
			
		||||
        util::Log() << "Generating edge-expanded graph representation";
 | 
			
		||||
    util::Log() << "Computing strictly connected components ...";
 | 
			
		||||
    FindComponents(max_edge_id, edge_based_edge_list, edge_based_node_list);
 | 
			
		||||
 | 
			
		||||
        TIMER_START(expansion);
 | 
			
		||||
    util::Log() << "Building r-tree ...";
 | 
			
		||||
    TIMER_START(rtree);
 | 
			
		||||
    BuildRTree(std::move(edge_based_node_list), std::move(node_is_startpoint), coordinates);
 | 
			
		||||
 | 
			
		||||
        std::vector<EdgeBasedNode> edge_based_node_list;
 | 
			
		||||
        util::DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
 | 
			
		||||
        std::vector<bool> node_is_startpoint;
 | 
			
		||||
        std::vector<EdgeWeight> edge_based_node_weights;
 | 
			
		||||
        std::vector<util::Coordinate> coordinates;
 | 
			
		||||
        extractor::PackedOSMIDs osm_node_ids;
 | 
			
		||||
    TIMER_STOP(rtree);
 | 
			
		||||
 | 
			
		||||
        auto graph_size = BuildEdgeExpandedGraph(scripting_environment,
 | 
			
		||||
                                                 coordinates,
 | 
			
		||||
                                                 osm_node_ids,
 | 
			
		||||
                                                 edge_based_node_list,
 | 
			
		||||
                                                 node_is_startpoint,
 | 
			
		||||
                                                 edge_based_node_weights,
 | 
			
		||||
                                                 edge_based_edge_list,
 | 
			
		||||
                                                 config.intersection_class_data_output_path);
 | 
			
		||||
    util::Log() << "Writing node map ...";
 | 
			
		||||
    files::writeNodes(config.node_output_path, coordinates, osm_node_ids);
 | 
			
		||||
 | 
			
		||||
        auto number_of_node_based_nodes = graph_size.first;
 | 
			
		||||
        auto max_edge_id = graph_size.second;
 | 
			
		||||
    WriteEdgeBasedGraph(config.edge_graph_output_path, max_edge_id, edge_based_edge_list);
 | 
			
		||||
 | 
			
		||||
        TIMER_STOP(expansion);
 | 
			
		||||
    const auto nodes_per_second =
 | 
			
		||||
        static_cast<std::uint64_t>(number_of_node_based_nodes / TIMER_SEC(expansion));
 | 
			
		||||
    const auto edges_per_second =
 | 
			
		||||
        static_cast<std::uint64_t>((max_edge_id + 1) / TIMER_SEC(expansion));
 | 
			
		||||
 | 
			
		||||
        util::Log() << "Saving edge-based node weights to file.";
 | 
			
		||||
        TIMER_START(timer_write_node_weights);
 | 
			
		||||
        {
 | 
			
		||||
            storage::io::FileWriter writer(config.edge_based_node_weights_output_path,
 | 
			
		||||
                                           storage::io::FileWriter::GenerateFingerprint);
 | 
			
		||||
            storage::serialization::write(writer, edge_based_node_weights);
 | 
			
		||||
        }
 | 
			
		||||
        TIMER_STOP(timer_write_node_weights);
 | 
			
		||||
        util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")";
 | 
			
		||||
 | 
			
		||||
        util::Log() << "Computing strictly connected components ...";
 | 
			
		||||
        FindComponents(max_edge_id, edge_based_edge_list, edge_based_node_list);
 | 
			
		||||
 | 
			
		||||
        util::Log() << "Building r-tree ...";
 | 
			
		||||
        TIMER_START(rtree);
 | 
			
		||||
        BuildRTree(std::move(edge_based_node_list), std::move(node_is_startpoint), coordinates);
 | 
			
		||||
 | 
			
		||||
        TIMER_STOP(rtree);
 | 
			
		||||
 | 
			
		||||
        util::Log() << "Writing node map ...";
 | 
			
		||||
        files::writeNodes(config.node_output_path, coordinates, osm_node_ids);
 | 
			
		||||
 | 
			
		||||
        WriteEdgeBasedGraph(config.edge_graph_output_path, max_edge_id, edge_based_edge_list);
 | 
			
		||||
 | 
			
		||||
        const auto nodes_per_second =
 | 
			
		||||
            static_cast<std::uint64_t>(number_of_node_based_nodes / TIMER_SEC(expansion));
 | 
			
		||||
        const auto edges_per_second =
 | 
			
		||||
            static_cast<std::uint64_t>((max_edge_id + 1) / TIMER_SEC(expansion));
 | 
			
		||||
 | 
			
		||||
        util::Log() << "Expansion: " << nodes_per_second << " nodes/sec and " << edges_per_second
 | 
			
		||||
                    << " edges/sec";
 | 
			
		||||
        util::Log() << "To prepare the data for routing, run: "
 | 
			
		||||
                    << "./osrm-contract " << config.output_file_name;
 | 
			
		||||
    }
 | 
			
		||||
    util::Log() << "Expansion: " << nodes_per_second << " nodes/sec and " << edges_per_second
 | 
			
		||||
                << " edges/sec";
 | 
			
		||||
    util::Log() << "To prepare the data for routing, run: "
 | 
			
		||||
                << "./osrm-contract " << config.output_file_name;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<TurnRestriction> Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment,
 | 
			
		||||
                                                     const unsigned number_of_threads)
 | 
			
		||||
{
 | 
			
		||||
    TIMER_START(extracting);
 | 
			
		||||
 | 
			
		||||
    util::Log() << "Input file: " << config.input_path.filename().string();
 | 
			
		||||
    if (!config.profile_path.empty())
 | 
			
		||||
    {
 | 
			
		||||
        util::Log() << "Profile: " << config.profile_path.filename().string();
 | 
			
		||||
    }
 | 
			
		||||
    util::Log() << "Threads: " << number_of_threads;
 | 
			
		||||
 | 
			
		||||
    const osmium::io::File input_file(config.input_path.string());
 | 
			
		||||
 | 
			
		||||
    osmium::io::Reader reader(
 | 
			
		||||
        input_file, (config.use_metadata ? osmium::io::read_meta::yes : osmium::io::read_meta::no));
 | 
			
		||||
 | 
			
		||||
    const osmium::io::Header header = reader.header();
 | 
			
		||||
 | 
			
		||||
    unsigned number_of_nodes = 0;
 | 
			
		||||
    unsigned number_of_ways = 0;
 | 
			
		||||
    unsigned number_of_relations = 0;
 | 
			
		||||
 | 
			
		||||
    util::Log() << "Parsing in progress..";
 | 
			
		||||
    TIMER_START(parsing);
 | 
			
		||||
 | 
			
		||||
    ExtractionContainers extraction_containers;
 | 
			
		||||
    auto extractor_callbacks = std::make_unique<ExtractorCallbacks>(
 | 
			
		||||
        extraction_containers, scripting_environment.GetProfileProperties());
 | 
			
		||||
 | 
			
		||||
    // setup raster sources
 | 
			
		||||
    scripting_environment.SetupSources();
 | 
			
		||||
 | 
			
		||||
    std::string generator = header.get("generator");
 | 
			
		||||
    if (generator.empty())
 | 
			
		||||
    {
 | 
			
		||||
        generator = "unknown tool";
 | 
			
		||||
    }
 | 
			
		||||
    util::Log() << "input file generated by " << generator;
 | 
			
		||||
 | 
			
		||||
    // write .timestamp data file
 | 
			
		||||
    std::string timestamp = header.get("osmosis_replication_timestamp");
 | 
			
		||||
    if (timestamp.empty())
 | 
			
		||||
    {
 | 
			
		||||
        timestamp = "n/a";
 | 
			
		||||
    }
 | 
			
		||||
    util::Log() << "timestamp: " << timestamp;
 | 
			
		||||
 | 
			
		||||
    storage::io::FileWriter timestamp_file(config.timestamp_file_name,
 | 
			
		||||
                                           storage::io::FileWriter::GenerateFingerprint);
 | 
			
		||||
 | 
			
		||||
    timestamp_file.WriteFrom(timestamp.c_str(), timestamp.length());
 | 
			
		||||
 | 
			
		||||
    // initialize vectors holding parsed objects
 | 
			
		||||
    tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
 | 
			
		||||
    tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
 | 
			
		||||
    tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> restrictions = scripting_environment.GetRestrictions();
 | 
			
		||||
    // setup restriction parser
 | 
			
		||||
    const RestrictionParser restriction_parser(
 | 
			
		||||
        scripting_environment.GetProfileProperties().use_turn_restrictions,
 | 
			
		||||
        config.parse_conditionals,
 | 
			
		||||
        restrictions);
 | 
			
		||||
 | 
			
		||||
    // create a vector of iterators into the buffer
 | 
			
		||||
    for (std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
 | 
			
		||||
         const osmium::memory::Buffer buffer = reader.read();
 | 
			
		||||
         osm_elements.clear())
 | 
			
		||||
    {
 | 
			
		||||
        for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
 | 
			
		||||
        {
 | 
			
		||||
            osm_elements.push_back(iter);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // clear resulting vectors
 | 
			
		||||
        resulting_nodes.clear();
 | 
			
		||||
        resulting_ways.clear();
 | 
			
		||||
        resulting_restrictions.clear();
 | 
			
		||||
 | 
			
		||||
        scripting_environment.ProcessElements(osm_elements,
 | 
			
		||||
                                              restriction_parser,
 | 
			
		||||
                                              resulting_nodes,
 | 
			
		||||
                                              resulting_ways,
 | 
			
		||||
                                              resulting_restrictions);
 | 
			
		||||
 | 
			
		||||
        number_of_nodes += resulting_nodes.size();
 | 
			
		||||
        // put parsed objects thru extractor callbacks
 | 
			
		||||
        for (const auto &result : resulting_nodes)
 | 
			
		||||
        {
 | 
			
		||||
            extractor_callbacks->ProcessNode(
 | 
			
		||||
                static_cast<const osmium::Node &>(*(osm_elements[result.first])), result.second);
 | 
			
		||||
        }
 | 
			
		||||
        number_of_ways += resulting_ways.size();
 | 
			
		||||
        for (const auto &result : resulting_ways)
 | 
			
		||||
        {
 | 
			
		||||
            extractor_callbacks->ProcessWay(
 | 
			
		||||
                static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
 | 
			
		||||
        }
 | 
			
		||||
        number_of_relations += resulting_restrictions.size();
 | 
			
		||||
        for (const auto &result : resulting_restrictions)
 | 
			
		||||
        {
 | 
			
		||||
            extractor_callbacks->ProcessRestriction(result);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    TIMER_STOP(parsing);
 | 
			
		||||
    util::Log() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
 | 
			
		||||
 | 
			
		||||
    util::Log() << "Raw input contains " << number_of_nodes << " nodes, " << number_of_ways
 | 
			
		||||
                << " ways, and " << number_of_relations << " relations";
 | 
			
		||||
 | 
			
		||||
    // take control over the turn lane map
 | 
			
		||||
    turn_lane_map = extractor_callbacks->moveOutLaneDescriptionMap();
 | 
			
		||||
 | 
			
		||||
    extractor_callbacks.reset();
 | 
			
		||||
 | 
			
		||||
    if (extraction_containers.all_edges_list.empty())
 | 
			
		||||
    {
 | 
			
		||||
        throw util::exception(std::string("There are no edges remaining after parsing.") +
 | 
			
		||||
                              SOURCE_REF);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extraction_containers.PrepareData(scripting_environment,
 | 
			
		||||
                                      config.output_file_name,
 | 
			
		||||
                                      config.restriction_file_name,
 | 
			
		||||
                                      config.names_file_name);
 | 
			
		||||
 | 
			
		||||
    WriteProfileProperties(config.profile_properties_output_path,
 | 
			
		||||
                           scripting_environment.GetProfileProperties());
 | 
			
		||||
 | 
			
		||||
    TIMER_STOP(extracting);
 | 
			
		||||
    util::Log() << "extraction finished after " << TIMER_SEC(extracting) << "s";
 | 
			
		||||
 | 
			
		||||
    return extraction_containers.unconditional_turn_restrictions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Extractor::WriteProfileProperties(const std::string &output_path,
 | 
			
		||||
                                       const ProfileProperties &properties) const
 | 
			
		||||
{
 | 
			
		||||
@ -384,22 +392,6 @@ void Extractor::FindComponents(unsigned max_edge_id,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  \brief Build load restrictions from .restriction file
 | 
			
		||||
  */
 | 
			
		||||
std::shared_ptr<RestrictionMap> Extractor::LoadRestrictionMap()
 | 
			
		||||
{
 | 
			
		||||
    storage::io::FileReader file_reader(config.restriction_file_name,
 | 
			
		||||
                                        storage::io::FileReader::VerifyFingerprint);
 | 
			
		||||
    std::vector<TurnRestriction> restriction_list;
 | 
			
		||||
 | 
			
		||||
    util::loadRestrictionsFromFile(file_reader, restriction_list);
 | 
			
		||||
 | 
			
		||||
    util::Log() << " - " << restriction_list.size() << " restrictions.";
 | 
			
		||||
 | 
			
		||||
    return std::make_shared<RestrictionMap>(restriction_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  \brief Load node based graph from .osrm file
 | 
			
		||||
  */
 | 
			
		||||
@ -444,12 +436,13 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment,
 | 
			
		||||
                                  std::vector<bool> &node_is_startpoint,
 | 
			
		||||
                                  std::vector<EdgeWeight> &edge_based_node_weights,
 | 
			
		||||
                                  util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
 | 
			
		||||
                                  const std::string &intersection_class_output_file)
 | 
			
		||||
                                  const std::string &intersection_class_output_file,
 | 
			
		||||
                                  std::vector<TurnRestriction> &turn_restrictions)
 | 
			
		||||
{
 | 
			
		||||
    std::unordered_set<NodeID> barrier_nodes;
 | 
			
		||||
    std::unordered_set<NodeID> traffic_lights;
 | 
			
		||||
 | 
			
		||||
    auto restriction_map = LoadRestrictionMap();
 | 
			
		||||
    auto restriction_map = std::make_shared<RestrictionMap>(turn_restrictions);
 | 
			
		||||
    auto node_based_graph =
 | 
			
		||||
        LoadNodeBasedGraph(barrier_nodes, traffic_lights, coordinates, osm_node_ids);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
#include "extractor/restriction_parser.hpp"
 | 
			
		||||
#include "extractor/profile_properties.hpp"
 | 
			
		||||
#include "extractor/scripting_environment.hpp"
 | 
			
		||||
 | 
			
		||||
#include "extractor/external_memory_node.hpp"
 | 
			
		||||
 | 
			
		||||
#include "util/conditional_restrictions.hpp"
 | 
			
		||||
#include "util/log.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string.hpp>
 | 
			
		||||
@ -24,12 +24,14 @@ namespace osrm
 | 
			
		||||
namespace extractor
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
RestrictionParser::RestrictionParser(ScriptingEnvironment &scripting_environment)
 | 
			
		||||
    : use_turn_restrictions(scripting_environment.GetProfileProperties().use_turn_restrictions)
 | 
			
		||||
RestrictionParser::RestrictionParser(bool use_turn_restrictions_,
 | 
			
		||||
                                     bool parse_conditionals_,
 | 
			
		||||
                                     std::vector<std::string> &restrictions_)
 | 
			
		||||
    : use_turn_restrictions(use_turn_restrictions_), parse_conditionals(parse_conditionals_),
 | 
			
		||||
      restrictions(restrictions_)
 | 
			
		||||
{
 | 
			
		||||
    if (use_turn_restrictions)
 | 
			
		||||
    {
 | 
			
		||||
        restrictions = scripting_environment.GetRestrictions();
 | 
			
		||||
        const unsigned count = restrictions.size();
 | 
			
		||||
        if (count > 0)
 | 
			
		||||
        {
 | 
			
		||||
@ -54,9 +56,10 @@ RestrictionParser::RestrictionParser(ScriptingEnvironment &scripting_environment
 | 
			
		||||
 * in the corresponding profile. We use it for both namespacing restrictions, as in
 | 
			
		||||
 * restriction:motorcar as well as whitelisting if its in except:motorcar.
 | 
			
		||||
 */
 | 
			
		||||
boost::optional<InputRestrictionContainer>
 | 
			
		||||
std::vector<InputRestrictionContainer>
 | 
			
		||||
RestrictionParser::TryParse(const osmium::Relation &relation) const
 | 
			
		||||
{
 | 
			
		||||
    std::vector<InputRestrictionContainer> parsed_restrictions;
 | 
			
		||||
    // return if turn restrictions should be ignored
 | 
			
		||||
    if (!use_turn_restrictions)
 | 
			
		||||
    {
 | 
			
		||||
@ -65,10 +68,21 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
 | 
			
		||||
 | 
			
		||||
    osmium::tags::KeyFilter filter(false);
 | 
			
		||||
    filter.add(true, "restriction");
 | 
			
		||||
    if (parse_conditionals)
 | 
			
		||||
    {
 | 
			
		||||
        filter.add(true, "restriction:conditional");
 | 
			
		||||
        for (const auto &namespaced : restrictions)
 | 
			
		||||
        {
 | 
			
		||||
            filter.add(true, "restriction:" + namespaced + ":conditional");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Not only use restriction= but also e.g. restriction:motorcar=
 | 
			
		||||
    // Include restriction:{mode}:conditional if flagged
 | 
			
		||||
    for (const auto &namespaced : restrictions)
 | 
			
		||||
    {
 | 
			
		||||
        filter.add(true, "restriction:" + namespaced);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const osmium::TagList &tag_list = relation.tags();
 | 
			
		||||
 | 
			
		||||
@ -160,7 +174,42 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return boost::make_optional(std::move(restriction_container));
 | 
			
		||||
 | 
			
		||||
    // parse conditional tags
 | 
			
		||||
    if (parse_conditionals)
 | 
			
		||||
    {
 | 
			
		||||
        osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
 | 
			
		||||
        osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
 | 
			
		||||
        for (; fi_begin != fi_end; ++fi_begin)
 | 
			
		||||
        {
 | 
			
		||||
            const std::string key(fi_begin->key());
 | 
			
		||||
            const std::string value(fi_begin->value());
 | 
			
		||||
 | 
			
		||||
            // Parse condition and add independent value/condition pairs
 | 
			
		||||
            const auto &parsed = osrm::util::ParseConditionalRestrictions(value);
 | 
			
		||||
 | 
			
		||||
            if (parsed.empty())
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            for (const auto &p : parsed)
 | 
			
		||||
            {
 | 
			
		||||
                std::vector<util::OpeningHours> hours = util::ParseOpeningHours(p.condition);
 | 
			
		||||
                // found unrecognized condition, continue
 | 
			
		||||
                if (hours.empty())
 | 
			
		||||
                    return {};
 | 
			
		||||
 | 
			
		||||
                restriction_container.restriction.condition = std::move(hours);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // push back a copy of turn restriction
 | 
			
		||||
    if (restriction_container.restriction.via.node != SPECIAL_NODEID &&
 | 
			
		||||
        restriction_container.restriction.from.node != SPECIAL_NODEID &&
 | 
			
		||||
        restriction_container.restriction.to.node != SPECIAL_NODEID)
 | 
			
		||||
        parsed_restrictions.push_back(restriction_container);
 | 
			
		||||
 | 
			
		||||
    return parsed_restrictions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_string) const
 | 
			
		||||
 | 
			
		||||
@ -498,6 +498,7 @@ void Sol2ScriptingEnvironment::ProcessElements(
 | 
			
		||||
        [&](const tbb::blocked_range<std::size_t> &range) {
 | 
			
		||||
            ExtractionNode result_node;
 | 
			
		||||
            ExtractionWay result_way;
 | 
			
		||||
            std::vector<InputRestrictionContainer> result_res;
 | 
			
		||||
            auto &local_context = this->GetSol2Context();
 | 
			
		||||
 | 
			
		||||
            for (auto x = range.begin(), end = range.end(); x != end; ++x)
 | 
			
		||||
@ -525,8 +526,13 @@ void Sol2ScriptingEnvironment::ProcessElements(
 | 
			
		||||
                    resulting_ways.push_back(std::make_pair(x, std::move(result_way)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case osmium::item_type::relation:
 | 
			
		||||
                    resulting_restrictions.push_back(restriction_parser.TryParse(
 | 
			
		||||
                        static_cast<const osmium::Relation &>(*entity)));
 | 
			
		||||
                    result_res.clear();
 | 
			
		||||
                    result_res =
 | 
			
		||||
                        restriction_parser.TryParse(static_cast<const osmium::Relation &>(*entity));
 | 
			
		||||
                    for (const InputRestrictionContainer &r : result_res)
 | 
			
		||||
                    {
 | 
			
		||||
                        resulting_restrictions.push_back(r);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@
 | 
			
		||||
#include "osrm/contractor.hpp"
 | 
			
		||||
#include "osrm/contractor_config.hpp"
 | 
			
		||||
#include "util/log.hpp"
 | 
			
		||||
#include "util/timezones.hpp"
 | 
			
		||||
#include "util/version.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
@ -61,7 +62,24 @@ return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig
 | 
			
		||||
            &contractor_config.updater_config.log_edge_updates_factor)
 | 
			
		||||
            ->default_value(0.0),
 | 
			
		||||
        "Use with `--segment-speed-file`. Provide an `x` factor, by which Extractor will log edge "
 | 
			
		||||
        "weights updated by more than this factor");
 | 
			
		||||
        "weights updated by more than this factor")(
 | 
			
		||||
        "parse-conditionals-from-now",
 | 
			
		||||
        boost::program_options::value<std::time_t>(&contractor_config.updater_config.valid_now)
 | 
			
		||||
            ->default_value(0),
 | 
			
		||||
        "Optional for conditional turn restriction parsing, provide a UTC time stamp from "
 | 
			
		||||
        "which "
 | 
			
		||||
        "to evaluate the validity of conditional turn restrictions");
 | 
			
		||||
 | 
			
		||||
    if (updater::SupportsShapefiles())
 | 
			
		||||
    {
 | 
			
		||||
        config_options.add_options()("time-zone-file",
 | 
			
		||||
                                     boost::program_options::value<std::string>(
 | 
			
		||||
                                         &contractor_config.updater_config.tz_file_path)
 | 
			
		||||
                                         ->default_value(""),
 | 
			
		||||
                                     "Required for conditional turn restriction parsing, provide a "
 | 
			
		||||
                                     "shp or dbf file containing "
 | 
			
		||||
                                     "time zone boundaries");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // hidden options, will be allowed on command line, but will not be shown to the user
 | 
			
		||||
    boost::program_options::options_description hidden_options("Hidden options");
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/program_options.hpp>
 | 
			
		||||
#include <util/timezones.hpp>
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
@ -51,7 +52,25 @@ parseArguments(int argc, char *argv[], customizer::CustomizationConfig &customiz
 | 
			
		||||
                           ->default_value(0.0),
 | 
			
		||||
                       "Use with `--segment-speed-file`. Provide an `x` factor, by which Extractor "
 | 
			
		||||
                       "will log edge "
 | 
			
		||||
                       "weights updated by more than this factor");
 | 
			
		||||
                       "weights updated by more than this factor")(
 | 
			
		||||
            "parse-conditionals-from-now",
 | 
			
		||||
            boost::program_options::value<std::time_t>(
 | 
			
		||||
                &customization_config.updater_config.valid_now)
 | 
			
		||||
                ->default_value(0),
 | 
			
		||||
            "Optional for conditional turn restriction parsing, provide a UTC time stamp from "
 | 
			
		||||
            "which "
 | 
			
		||||
            "to evaluate the validity of conditional turn restrictions");
 | 
			
		||||
 | 
			
		||||
    if (updater::SupportsShapefiles())
 | 
			
		||||
    {
 | 
			
		||||
        config_options.add_options()("time-zone-file",
 | 
			
		||||
                                     boost::program_options::value<std::string>(
 | 
			
		||||
                                         &customization_config.updater_config.tz_file_path)
 | 
			
		||||
                                         ->default_value(""),
 | 
			
		||||
                                     "Required for conditional turn restriction parsing, provide a "
 | 
			
		||||
                                     "shp or dbf file containing "
 | 
			
		||||
                                     "time zone boundaries");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // hidden options, will be allowed on command line, but will not be
 | 
			
		||||
    // shown to the user
 | 
			
		||||
 | 
			
		||||
@ -1,35 +1,13 @@
 | 
			
		||||
#include "util/conditional_restrictions.hpp"
 | 
			
		||||
#include "tools/extract-conditionals.hpp"
 | 
			
		||||
 | 
			
		||||
#include "util/exception.hpp"
 | 
			
		||||
#include "util/for_each_pair.hpp"
 | 
			
		||||
#include "util/log.hpp"
 | 
			
		||||
#include "util/opening_hours.hpp"
 | 
			
		||||
#include "util/timezones.hpp"
 | 
			
		||||
#include "util/version.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/algorithm/string/predicate.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/fusion/include/adapt_adt.hpp>
 | 
			
		||||
#include <boost/geometry.hpp>
 | 
			
		||||
#include <boost/geometry/index/rtree.hpp>
 | 
			
		||||
#include <boost/program_options.hpp>
 | 
			
		||||
#include <boost/scope_exit.hpp>
 | 
			
		||||
#include <boost/spirit/include/qi.hpp>
 | 
			
		||||
#include <boost/spirit/include/support_line_pos_iterator.hpp>
 | 
			
		||||
 | 
			
		||||
#include <osmium/handler.hpp>
 | 
			
		||||
#include <osmium/index/map/sparse_mem_array.hpp>
 | 
			
		||||
#include <osmium/io/any_input.hpp>
 | 
			
		||||
#include <osmium/tags/regex_filter.hpp>
 | 
			
		||||
#include <osmium/visitor.hpp>
 | 
			
		||||
 | 
			
		||||
#include <shapefil.h>
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
//#include <ctime>
 | 
			
		||||
 | 
			
		||||
// Program arguments parsing functions
 | 
			
		||||
@ -181,23 +159,6 @@ void ParseCheckCommandArguments(const char *executable,
 | 
			
		||||
    po::notify(vm);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Data types and functions for conditional restriction
 | 
			
		||||
struct ConditionalRestriction
 | 
			
		||||
{
 | 
			
		||||
    osmium::object_id_type from;
 | 
			
		||||
    osmium::object_id_type via;
 | 
			
		||||
    osmium::object_id_type to;
 | 
			
		||||
    std::string tag;
 | 
			
		||||
    std::string value;
 | 
			
		||||
    std::string condition;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct LocatedConditionalRestriction
 | 
			
		||||
{
 | 
			
		||||
    osmium::Location location;
 | 
			
		||||
    ConditionalRestriction restriction;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
BOOST_FUSION_ADAPT_ADT(LocatedConditionalRestriction,
 | 
			
		||||
  (osmium::object_id_type, osmium::object_id_type, obj.restriction.from, obj.restriction.from = val)
 | 
			
		||||
@ -210,189 +171,6 @@ BOOST_FUSION_ADAPT_ADT(LocatedConditionalRestriction,
 | 
			
		||||
  (std::int32_t, std::int32_t, obj.location.lat(), obj.location.set_lat(val)))
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
// The first pass relations handler that collects conditional restrictions
 | 
			
		||||
class ConditionalRestrictionsCollector : public osmium::handler::Handler
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
    ConditionalRestrictionsCollector(std::vector<ConditionalRestriction> &restrictions)
 | 
			
		||||
        : restrictions(restrictions)
 | 
			
		||||
    {
 | 
			
		||||
        tag_filter.add(true, std::regex("^restriction.*:conditional$"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void relation(const osmium::Relation &relation) const
 | 
			
		||||
    {
 | 
			
		||||
        // Check if relation contains any ":conditional" tag
 | 
			
		||||
        const osmium::TagList &tags = relation.tags();
 | 
			
		||||
        typename decltype(tag_filter)::iterator first(tag_filter, tags.begin(), tags.end());
 | 
			
		||||
        typename decltype(tag_filter)::iterator last(tag_filter, tags.end(), tags.end());
 | 
			
		||||
        if (first == last)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Get member references of from(way) -> via(node) -> to(way)
 | 
			
		||||
        auto from = invalid_id, via = invalid_id, to = invalid_id;
 | 
			
		||||
        for (const auto &member : relation.members())
 | 
			
		||||
        {
 | 
			
		||||
            if (member.ref() == 0)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (member.type() == osmium::item_type::node && strcmp(member.role(), "via") == 0)
 | 
			
		||||
            {
 | 
			
		||||
                via = member.ref();
 | 
			
		||||
            }
 | 
			
		||||
            else if (member.type() == osmium::item_type::way && strcmp(member.role(), "from") == 0)
 | 
			
		||||
            {
 | 
			
		||||
                from = member.ref();
 | 
			
		||||
            }
 | 
			
		||||
            else if (member.type() == osmium::item_type::way && strcmp(member.role(), "to") == 0)
 | 
			
		||||
            {
 | 
			
		||||
                to = member.ref();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (from == invalid_id || via == invalid_id || to == invalid_id)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (; first != last; ++first)
 | 
			
		||||
        {
 | 
			
		||||
            // Parse condition and add independent value/condition pairs
 | 
			
		||||
            const auto &parsed = osrm::util::ParseConditionalRestrictions(first->value());
 | 
			
		||||
 | 
			
		||||
            if (parsed.empty())
 | 
			
		||||
            {
 | 
			
		||||
                osrm::util::Log(logWARNING) << "Conditional restriction parsing failed for \""
 | 
			
		||||
                                            << first->value() << "\" at the turn " << from << " -> "
 | 
			
		||||
                                            << via << " -> " << to;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (auto &restriction : parsed)
 | 
			
		||||
            {
 | 
			
		||||
                restrictions.push_back(
 | 
			
		||||
                    {from, via, to, first->key(), restriction.value, restriction.condition});
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    const osmium::object_id_type invalid_id = std::numeric_limits<osmium::object_id_type>::max();
 | 
			
		||||
    osmium::tags::Filter<std::regex> tag_filter;
 | 
			
		||||
    std::vector<ConditionalRestriction> &restrictions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// The second pass handler that collects related nodes and ways.
 | 
			
		||||
// process_restrictions method calls for every collected conditional restriction
 | 
			
		||||
// a callback function with the prototype:
 | 
			
		||||
//    (location, from node, via node, to node, tag, value, condition)
 | 
			
		||||
// If the restriction value starts with "only_" the callback function will be called
 | 
			
		||||
// for every edge adjacent to `via` node but not containing `to` node.
 | 
			
		||||
class ConditionalRestrictionsHandler : public osmium::handler::Handler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    ConditionalRestrictionsHandler(const std::vector<ConditionalRestriction> &restrictions)
 | 
			
		||||
        : restrictions(restrictions)
 | 
			
		||||
    {
 | 
			
		||||
        for (auto &restriction : restrictions)
 | 
			
		||||
            related_vias.insert(restriction.via);
 | 
			
		||||
 | 
			
		||||
        via_adjacency.reserve(restrictions.size() * 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void node(const osmium::Node &node)
 | 
			
		||||
    {
 | 
			
		||||
        const osmium::object_id_type id = node.id();
 | 
			
		||||
        if (related_vias.find(id) != related_vias.end())
 | 
			
		||||
        {
 | 
			
		||||
            location_storage.set(static_cast<osmium::unsigned_object_id_type>(id), node.location());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void way(const osmium::Way &way)
 | 
			
		||||
    {
 | 
			
		||||
        const auto &nodes = way.nodes();
 | 
			
		||||
 | 
			
		||||
        if (related_vias.find(nodes.front().ref()) != related_vias.end())
 | 
			
		||||
            via_adjacency.push_back(std::make_tuple(nodes.front().ref(), way.id(), nodes[1].ref()));
 | 
			
		||||
 | 
			
		||||
        if (related_vias.find(nodes.back().ref()) != related_vias.end())
 | 
			
		||||
            via_adjacency.push_back(
 | 
			
		||||
                std::make_tuple(nodes.back().ref(), way.id(), nodes[nodes.size() - 2].ref()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename Callback> void process(Callback callback)
 | 
			
		||||
    {
 | 
			
		||||
        location_storage.sort();
 | 
			
		||||
        std::sort(via_adjacency.begin(), via_adjacency.end());
 | 
			
		||||
 | 
			
		||||
        auto adjacent_nodes = [this](auto node) {
 | 
			
		||||
            auto first = std::lower_bound(
 | 
			
		||||
                via_adjacency.begin(), via_adjacency.end(), adjacency_type{node, 0, 0});
 | 
			
		||||
            auto last =
 | 
			
		||||
                std::upper_bound(first, via_adjacency.end(), adjacency_type{node + 1, 0, 0});
 | 
			
		||||
            return std::make_pair(first, last);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        auto find_node = [](auto nodes, auto way) {
 | 
			
		||||
            return std::find_if(
 | 
			
		||||
                nodes.first, nodes.second, [way](const auto &n) { return std::get<1>(n) == way; });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        for (auto &restriction : restrictions)
 | 
			
		||||
        {
 | 
			
		||||
            auto nodes = adjacent_nodes(restriction.via);
 | 
			
		||||
            auto from = find_node(nodes, restriction.from);
 | 
			
		||||
            if (from == nodes.second)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            const auto &location =
 | 
			
		||||
                location_storage.get(static_cast<osmium::unsigned_object_id_type>(restriction.via));
 | 
			
		||||
 | 
			
		||||
            if (boost::algorithm::starts_with(restriction.value, "only_"))
 | 
			
		||||
            {
 | 
			
		||||
                for (auto it = nodes.first; it != nodes.second; ++it)
 | 
			
		||||
                {
 | 
			
		||||
                    if (std::get<1>(*it) == restriction.to)
 | 
			
		||||
                        continue;
 | 
			
		||||
 | 
			
		||||
                    callback(location,
 | 
			
		||||
                             std::get<2>(*from),
 | 
			
		||||
                             restriction.via,
 | 
			
		||||
                             std::get<2>(*it),
 | 
			
		||||
                             restriction.tag,
 | 
			
		||||
                             restriction.value,
 | 
			
		||||
                             restriction.condition);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                auto to = find_node(nodes, restriction.to);
 | 
			
		||||
                if (to != nodes.second)
 | 
			
		||||
                {
 | 
			
		||||
                    callback(location,
 | 
			
		||||
                             std::get<2>(*from),
 | 
			
		||||
                             restriction.via,
 | 
			
		||||
                             std::get<2>(*to),
 | 
			
		||||
                             restriction.tag,
 | 
			
		||||
                             restriction.value,
 | 
			
		||||
                             restriction.condition);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    using index_type =
 | 
			
		||||
        osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
 | 
			
		||||
    using adjacency_type =
 | 
			
		||||
        std::tuple<osmium::object_id_type, osmium::object_id_type, osmium::object_id_type>;
 | 
			
		||||
 | 
			
		||||
    const std::vector<ConditionalRestriction> &restrictions;
 | 
			
		||||
    std::unordered_set<osmium::object_id_type> related_vias;
 | 
			
		||||
    std::vector<adjacency_type> via_adjacency;
 | 
			
		||||
    index_type location_storage;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int RestrictionsDumpCommand(const char *executable, const std::vector<std::string> &arguments)
 | 
			
		||||
{
 | 
			
		||||
    std::string osm_filename, csv_filename;
 | 
			
		||||
@ -444,22 +222,6 @@ int RestrictionsDumpCommand(const char *executable, const std::vector<std::strin
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Data types and functions for conditional speed limits
 | 
			
		||||
struct ConditionalSpeedLimit
 | 
			
		||||
{
 | 
			
		||||
    osmium::object_id_type from;
 | 
			
		||||
    osmium::object_id_type to;
 | 
			
		||||
    std::string tag;
 | 
			
		||||
    int value;
 | 
			
		||||
    std::string condition;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct LocatedConditionalSpeedLimit
 | 
			
		||||
{
 | 
			
		||||
    osmium::Location location;
 | 
			
		||||
    ConditionalSpeedLimit speed_limit;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
BOOST_FUSION_ADAPT_ADT(LocatedConditionalSpeedLimit,
 | 
			
		||||
  (osmium::object_id_type, osmium::object_id_type, obj.speed_limit.from, obj.speed_limit.from = val)
 | 
			
		||||
@ -471,136 +233,6 @@ BOOST_FUSION_ADAPT_ADT(LocatedConditionalSpeedLimit,
 | 
			
		||||
  (std::int32_t, std::int32_t, obj.location.lat(), obj.location.set_lat(val)))
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
class ConditionalSpeedLimitsCollector : public osmium::handler::Handler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    ConditionalSpeedLimitsCollector()
 | 
			
		||||
    {
 | 
			
		||||
        tag_filter.add(true, std::regex("^maxspeed.*:conditional$"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void node(const osmium::Node &node)
 | 
			
		||||
    {
 | 
			
		||||
        const osmium::object_id_type id = node.id();
 | 
			
		||||
        if (related_nodes.find(id) != related_nodes.end())
 | 
			
		||||
        {
 | 
			
		||||
            location_storage.set(static_cast<osmium::unsigned_object_id_type>(id), node.location());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void way(const osmium::Way &way)
 | 
			
		||||
    {
 | 
			
		||||
        const osmium::TagList &tags = way.tags();
 | 
			
		||||
        typename decltype(tag_filter)::iterator first(tag_filter, tags.begin(), tags.end());
 | 
			
		||||
        typename decltype(tag_filter)::iterator last(tag_filter, tags.end(), tags.end());
 | 
			
		||||
        if (first == last)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (; first != last; ++first)
 | 
			
		||||
        {
 | 
			
		||||
            // Parse condition and add independent value/condition pairs
 | 
			
		||||
            const auto &parsed = osrm::util::ParseConditionalRestrictions(first->value());
 | 
			
		||||
 | 
			
		||||
            if (parsed.empty())
 | 
			
		||||
            {
 | 
			
		||||
                osrm::util::Log(logWARNING) << "Conditional speed limit parsing failed for \""
 | 
			
		||||
                                            << first->value() << "\" on the way " << way.id();
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Collect node IDs for the second pass over nodes
 | 
			
		||||
            std::transform(way.nodes().begin(),
 | 
			
		||||
                           way.nodes().end(),
 | 
			
		||||
                           std::inserter(related_nodes, related_nodes.end()),
 | 
			
		||||
                           [](const auto &node_ref) { return node_ref.ref(); });
 | 
			
		||||
 | 
			
		||||
            // Collect speed limits
 | 
			
		||||
            for (auto &speed_limit : parsed)
 | 
			
		||||
            {
 | 
			
		||||
                // Convert value to an integer
 | 
			
		||||
                int speed_limit_value;
 | 
			
		||||
                {
 | 
			
		||||
                    namespace qi = boost::spirit::qi;
 | 
			
		||||
                    std::string::const_iterator first(speed_limit.value.begin()),
 | 
			
		||||
                        last(speed_limit.value.end());
 | 
			
		||||
                    if (!qi::parse(first, last, qi::int_, speed_limit_value) || first != last)
 | 
			
		||||
                    {
 | 
			
		||||
                        osrm::util::Log(logWARNING)
 | 
			
		||||
                            << "Conditional speed limit has non-integer value \""
 | 
			
		||||
                            << speed_limit.value << "\" on the way " << way.id();
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                std::string key = first->key();
 | 
			
		||||
                const bool is_forward = key.find(":forward:") != std::string::npos;
 | 
			
		||||
                const bool is_backward = key.find(":backward:") != std::string::npos;
 | 
			
		||||
                const bool is_direction_defined = is_forward || is_backward;
 | 
			
		||||
 | 
			
		||||
                if (is_forward || !is_direction_defined)
 | 
			
		||||
                {
 | 
			
		||||
                    osrm::util::for_each_pair(way.nodes().cbegin(),
 | 
			
		||||
                                              way.nodes().cend(),
 | 
			
		||||
                                              [&](const auto &from, const auto &to) {
 | 
			
		||||
                                                  speed_limits.push_back(
 | 
			
		||||
                                                      ConditionalSpeedLimit{from.ref(),
 | 
			
		||||
                                                                            to.ref(),
 | 
			
		||||
                                                                            key,
 | 
			
		||||
                                                                            speed_limit_value,
 | 
			
		||||
                                                                            speed_limit.condition});
 | 
			
		||||
                                              });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (is_backward || !is_direction_defined)
 | 
			
		||||
                {
 | 
			
		||||
                    osrm::util::for_each_pair(way.nodes().cbegin(),
 | 
			
		||||
                                              way.nodes().cend(),
 | 
			
		||||
                                              [&](const auto &to, const auto &from) {
 | 
			
		||||
                                                  speed_limits.push_back(
 | 
			
		||||
                                                      ConditionalSpeedLimit{from.ref(),
 | 
			
		||||
                                                                            to.ref(),
 | 
			
		||||
                                                                            key,
 | 
			
		||||
                                                                            speed_limit_value,
 | 
			
		||||
                                                                            speed_limit.condition});
 | 
			
		||||
                                              });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename Callback> void process(Callback callback)
 | 
			
		||||
    {
 | 
			
		||||
        location_storage.sort();
 | 
			
		||||
 | 
			
		||||
        for (auto &speed_limit : speed_limits)
 | 
			
		||||
        {
 | 
			
		||||
            const auto &location_from = location_storage.get(
 | 
			
		||||
                static_cast<osmium::unsigned_object_id_type>(speed_limit.from));
 | 
			
		||||
            const auto &location_to =
 | 
			
		||||
                location_storage.get(static_cast<osmium::unsigned_object_id_type>(speed_limit.to));
 | 
			
		||||
            osmium::Location location{(location_from.lon() + location_to.lon()) / 2,
 | 
			
		||||
                                      (location_from.lat() + location_to.lat()) / 2};
 | 
			
		||||
 | 
			
		||||
            callback(location,
 | 
			
		||||
                     speed_limit.from,
 | 
			
		||||
                     speed_limit.to,
 | 
			
		||||
                     speed_limit.tag,
 | 
			
		||||
                     speed_limit.value,
 | 
			
		||||
                     speed_limit.condition);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    using index_type =
 | 
			
		||||
        osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
 | 
			
		||||
 | 
			
		||||
    osmium::tags::Filter<std::regex> tag_filter;
 | 
			
		||||
    std::unordered_set<osmium::object_id_type> related_nodes;
 | 
			
		||||
    std::vector<ConditionalSpeedLimit> speed_limits;
 | 
			
		||||
    index_type location_storage;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int SpeedLimitsDumpCommand(const char *executable, const std::vector<std::string> &arguments)
 | 
			
		||||
{
 | 
			
		||||
    std::string osm_filename, csv_filename;
 | 
			
		||||
@ -646,119 +278,6 @@ int SpeedLimitsDumpCommand(const char *executable, const std::vector<std::string
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Time zone shape polygons loaded in R-tree
 | 
			
		||||
// local_time_t is a pair of a time zone shape polygon and the corresponding local time
 | 
			
		||||
// rtree_t is a lookup R-tree that maps a geographic point to an index in a local_time_t vector
 | 
			
		||||
using point_t = boost::geometry::model::
 | 
			
		||||
    point<double, 2, boost::geometry::cs::spherical_equatorial<boost::geometry::degree>>;
 | 
			
		||||
using polygon_t = boost::geometry::model::polygon<point_t>;
 | 
			
		||||
using box_t = boost::geometry::model::box<point_t>;
 | 
			
		||||
using rtree_t =
 | 
			
		||||
    boost::geometry::index::rtree<std::pair<box_t, size_t>, boost::geometry::index::rstar<8>>;
 | 
			
		||||
using local_time_t = std::pair<polygon_t, struct tm>;
 | 
			
		||||
 | 
			
		||||
// Function loads time zone shape polygons, computes a zone local time for utc_time,
 | 
			
		||||
// creates a lookup R-tree and returns a lambda function that maps a point
 | 
			
		||||
// to the corresponding local time
 | 
			
		||||
auto LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time)
 | 
			
		||||
{
 | 
			
		||||
    // Load time zones shapes and collect local times of utc_time
 | 
			
		||||
    auto shphandle = SHPOpen(tz_shapes_filename.c_str(), "rb");
 | 
			
		||||
    auto dbfhandle = DBFOpen(tz_shapes_filename.c_str(), "rb");
 | 
			
		||||
 | 
			
		||||
    BOOST_SCOPE_EXIT(&shphandle, &dbfhandle)
 | 
			
		||||
    {
 | 
			
		||||
        DBFClose(dbfhandle);
 | 
			
		||||
        SHPClose(shphandle);
 | 
			
		||||
    }
 | 
			
		||||
    BOOST_SCOPE_EXIT_END
 | 
			
		||||
 | 
			
		||||
    if (!shphandle || !dbfhandle)
 | 
			
		||||
    {
 | 
			
		||||
        throw osrm::util::exception("failed to open " + tz_shapes_filename + ".shp or " +
 | 
			
		||||
                                    tz_shapes_filename + ".dbf file");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int num_entities, shape_type;
 | 
			
		||||
    SHPGetInfo(shphandle, &num_entities, &shape_type, NULL, NULL);
 | 
			
		||||
    if (num_entities != DBFGetRecordCount(dbfhandle))
 | 
			
		||||
    {
 | 
			
		||||
        throw osrm::util::exception("inconsistent " + tz_shapes_filename + ".shp and " +
 | 
			
		||||
                                    tz_shapes_filename + ".dbf files");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto tzid = DBFGetFieldIndex(dbfhandle, "TZID");
 | 
			
		||||
    if (tzid == -1)
 | 
			
		||||
    {
 | 
			
		||||
        throw osrm::util::exception("did not find field called 'TZID' in the " +
 | 
			
		||||
                                    tz_shapes_filename + ".dbf file");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Lambda function that returns local time in the tzname time zone
 | 
			
		||||
    // Thread safety: MT-Unsafe const:env
 | 
			
		||||
    std::unordered_map<std::string, struct tm> local_time_memo;
 | 
			
		||||
    auto get_local_time_in_tz = [utc_time, &local_time_memo](const char *tzname) {
 | 
			
		||||
        auto it = local_time_memo.find(tzname);
 | 
			
		||||
        if (it == local_time_memo.end())
 | 
			
		||||
        {
 | 
			
		||||
            struct tm timeinfo;
 | 
			
		||||
            setenv("TZ", tzname, 1);
 | 
			
		||||
            tzset();
 | 
			
		||||
            localtime_r(&utc_time, &timeinfo);
 | 
			
		||||
            it = local_time_memo.insert({tzname, timeinfo}).first;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return it->second;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Get all time zone shapes and save local times in a vector
 | 
			
		||||
    std::vector<rtree_t::value_type> polygons;
 | 
			
		||||
    std::vector<local_time_t> local_times;
 | 
			
		||||
    for (int shape = 0; shape < num_entities; ++shape)
 | 
			
		||||
    {
 | 
			
		||||
        auto object = SHPReadObject(shphandle, shape);
 | 
			
		||||
        BOOST_SCOPE_EXIT(&object) { SHPDestroyObject(object); }
 | 
			
		||||
        BOOST_SCOPE_EXIT_END
 | 
			
		||||
 | 
			
		||||
        if (object && object->nSHPType == SHPT_POLYGON)
 | 
			
		||||
        {
 | 
			
		||||
            // Find time zone polygon and place its bbox in into R-Tree
 | 
			
		||||
            polygon_t polygon;
 | 
			
		||||
            for (int vertex = 0; vertex < object->nVertices; ++vertex)
 | 
			
		||||
            {
 | 
			
		||||
                polygon.outer().emplace_back(object->padfX[vertex], object->padfY[vertex]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            polygons.emplace_back(boost::geometry::return_envelope<box_t>(polygon),
 | 
			
		||||
                                  local_times.size());
 | 
			
		||||
 | 
			
		||||
            // Get time zone name and emplace polygon and local time for the UTC input
 | 
			
		||||
            const auto tzname = DBFReadStringAttribute(dbfhandle, shape, tzid);
 | 
			
		||||
            local_times.emplace_back(local_time_t{polygon, get_local_time_in_tz(tzname)});
 | 
			
		||||
 | 
			
		||||
            // std::cout << boost::geometry::dsv(boost::geometry::return_envelope<box_t>(polygon))
 | 
			
		||||
            //           << " " << tzname << " " << asctime(&local_times.back().second);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create R-tree for collected shape polygons
 | 
			
		||||
    rtree_t rtree(polygons);
 | 
			
		||||
 | 
			
		||||
    // Return a lambda function that maps the input point and UTC time to the local time
 | 
			
		||||
    // binds rtree and local_times
 | 
			
		||||
    return [rtree, local_times](const point_t &point) {
 | 
			
		||||
        std::vector<rtree_t::value_type> result;
 | 
			
		||||
        rtree.query(boost::geometry::index::intersects(point), std::back_inserter(result));
 | 
			
		||||
        for (const auto v : result)
 | 
			
		||||
        {
 | 
			
		||||
            const auto index = v.second;
 | 
			
		||||
            if (boost::geometry::within(point, local_times[index].first))
 | 
			
		||||
                return local_times[index].second;
 | 
			
		||||
        }
 | 
			
		||||
        return tm{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int RestrictionsCheckCommand(const char *executable, const std::vector<std::string> &arguments)
 | 
			
		||||
{
 | 
			
		||||
    std::string input_filename, output_filename;
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,13 @@ return_code parseArguments(int argc, char *argv[], extractor::ExtractorConfig &e
 | 
			
		||||
        boost::program_options::bool_switch(&extractor_config.use_metadata)
 | 
			
		||||
            ->implicit_value(true)
 | 
			
		||||
            ->default_value(false),
 | 
			
		||||
        "Use metada during osm parsing (This can affect the extraction performance).");
 | 
			
		||||
        "Use metadata during osm parsing (This can affect the extraction performance).")(
 | 
			
		||||
        "parse-conditional-restrictions",
 | 
			
		||||
        boost::program_options::value<bool>(&extractor_config.parse_conditionals)
 | 
			
		||||
            ->implicit_value(true)
 | 
			
		||||
            ->default_value(false),
 | 
			
		||||
        "Save conditional restrictions found during extraction to disk for use "
 | 
			
		||||
        "during contraction");
 | 
			
		||||
 | 
			
		||||
    bool dummy;
 | 
			
		||||
    // hidden options, will be allowed on command line, but will not be
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
#include "updater/updater.hpp"
 | 
			
		||||
 | 
			
		||||
#include "updater/csv_source.hpp"
 | 
			
		||||
 | 
			
		||||
#include "extractor/compressed_edge_container.hpp"
 | 
			
		||||
#include "extractor/edge_based_graph_factory.hpp"
 | 
			
		||||
#include "extractor/files.hpp"
 | 
			
		||||
#include "extractor/node_based_edge.hpp"
 | 
			
		||||
#include "extractor/packed_osm_ids.hpp"
 | 
			
		||||
#include "extractor/restriction.hpp"
 | 
			
		||||
 | 
			
		||||
#include "storage/io.hpp"
 | 
			
		||||
 | 
			
		||||
@ -15,15 +16,18 @@
 | 
			
		||||
#include "util/graph_loader.hpp"
 | 
			
		||||
#include "util/integer_range.hpp"
 | 
			
		||||
#include "util/log.hpp"
 | 
			
		||||
#include "util/opening_hours.hpp"
 | 
			
		||||
#include "util/static_graph.hpp"
 | 
			
		||||
#include "util/static_rtree.hpp"
 | 
			
		||||
#include "util/string_util.hpp"
 | 
			
		||||
#include "util/timezones.hpp"
 | 
			
		||||
#include "util/timing_util.hpp"
 | 
			
		||||
#include "util/typedefs.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/assert.hpp>
 | 
			
		||||
#include <boost/filesystem/fstream.hpp>
 | 
			
		||||
#include <boost/functional/hash.hpp>
 | 
			
		||||
#include <boost/geometry.hpp>
 | 
			
		||||
#include <boost/geometry/index/rtree.hpp>
 | 
			
		||||
#include <boost/interprocess/file_mapping.hpp>
 | 
			
		||||
#include <boost/interprocess/mapped_region.hpp>
 | 
			
		||||
 | 
			
		||||
@ -44,6 +48,25 @@
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace std
 | 
			
		||||
{
 | 
			
		||||
template <typename T1, typename T2, typename T3> struct hash<std::tuple<T1, T2, T3>>
 | 
			
		||||
{
 | 
			
		||||
    size_t operator()(const std::tuple<T1, T2, T3> &t) const
 | 
			
		||||
    {
 | 
			
		||||
        return hash_val(std::get<0>(t), std::get<1>(t), std::get<2>(t));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T1, typename T2> struct hash<std::tuple<T1, T2>>
 | 
			
		||||
{
 | 
			
		||||
    size_t operator()(const std::tuple<T1, T2> &t) const
 | 
			
		||||
    {
 | 
			
		||||
        return hash_val(std::get<0>(t), std::get<1>(t));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace updater
 | 
			
		||||
@ -135,12 +158,10 @@ tbb::concurrent_vector<GeometryID>
 | 
			
		||||
updateSegmentData(const UpdaterConfig &config,
 | 
			
		||||
                  const extractor::ProfileProperties &profile_properties,
 | 
			
		||||
                  const SegmentLookupTable &segment_speed_lookup,
 | 
			
		||||
                  extractor::SegmentDataContainer &segment_data)
 | 
			
		||||
                  extractor::SegmentDataContainer &segment_data,
 | 
			
		||||
                  std::vector<util::Coordinate> &coordinates,
 | 
			
		||||
                  extractor::PackedOSMIDs &osm_node_ids)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<util::Coordinate> coordinates;
 | 
			
		||||
    extractor::PackedOSMIDs osm_node_ids;
 | 
			
		||||
    extractor::files::readNodes(config.node_based_graph_path, coordinates, osm_node_ids);
 | 
			
		||||
 | 
			
		||||
    // vector to count used speeds for logging
 | 
			
		||||
    // size offset by one since index 0 is used for speeds not from external file
 | 
			
		||||
    using counters_type = std::vector<std::size_t>;
 | 
			
		||||
@ -379,26 +400,33 @@ updateTurnPenalties(const UpdaterConfig &config,
 | 
			
		||||
                    const extractor::ProfileProperties &profile_properties,
 | 
			
		||||
                    const TurnLookupTable &turn_penalty_lookup,
 | 
			
		||||
                    std::vector<TurnPenalty> &turn_weight_penalties,
 | 
			
		||||
                    std::vector<TurnPenalty> &turn_duration_penalties)
 | 
			
		||||
                    std::vector<TurnPenalty> &turn_duration_penalties,
 | 
			
		||||
                    extractor::PackedOSMIDs osm_node_ids)
 | 
			
		||||
{
 | 
			
		||||
    const auto weight_multiplier = profile_properties.GetWeightMultiplier();
 | 
			
		||||
    const auto turn_penalties_index_region =
 | 
			
		||||
    const auto turn_index_region =
 | 
			
		||||
        mmapFile(config.turn_penalties_index_path, boost::interprocess::read_only);
 | 
			
		||||
 | 
			
		||||
    // Mapped file pointer for turn indices
 | 
			
		||||
    const extractor::lookup::TurnIndexBlock *turn_index_blocks =
 | 
			
		||||
        reinterpret_cast<const extractor::lookup::TurnIndexBlock *>(
 | 
			
		||||
            turn_penalties_index_region.get_address());
 | 
			
		||||
            turn_index_region.get_address());
 | 
			
		||||
    BOOST_ASSERT(is_aligned<extractor::lookup::TurnIndexBlock>(turn_index_blocks));
 | 
			
		||||
 | 
			
		||||
    // Get the turn penalty and update to the new value if required
 | 
			
		||||
    std::vector<std::uint64_t> updated_turns;
 | 
			
		||||
    for (std::uint64_t edge_index = 0; edge_index < turn_weight_penalties.size(); ++edge_index)
 | 
			
		||||
    {
 | 
			
		||||
        // Get the turn penalty and update to the new value if required
 | 
			
		||||
        const auto &turn_index = turn_index_blocks[edge_index];
 | 
			
		||||
        // edges are stored by internal OSRM ids, these need to be mapped back to OSM ids
 | 
			
		||||
        const extractor::lookup::TurnIndexBlock internal_turn = turn_index_blocks[edge_index];
 | 
			
		||||
        const Turn osm_turn{osm_node_ids[internal_turn.from_id],
 | 
			
		||||
                            osm_node_ids[internal_turn.via_id],
 | 
			
		||||
                            osm_node_ids[internal_turn.to_id]};
 | 
			
		||||
        // original turn weight/duration values
 | 
			
		||||
        auto turn_weight_penalty = turn_weight_penalties[edge_index];
 | 
			
		||||
        auto turn_duration_penalty = turn_duration_penalties[edge_index];
 | 
			
		||||
        if (auto value = turn_penalty_lookup(turn_index))
 | 
			
		||||
 | 
			
		||||
        if (auto value = turn_penalty_lookup(osm_turn))
 | 
			
		||||
        {
 | 
			
		||||
            turn_duration_penalty =
 | 
			
		||||
                boost::numeric_cast<TurnPenalty>(std::round(value->duration * 10.));
 | 
			
		||||
@ -413,9 +441,113 @@ updateTurnPenalties(const UpdaterConfig &config,
 | 
			
		||||
 | 
			
		||||
        if (turn_weight_penalty < 0)
 | 
			
		||||
        {
 | 
			
		||||
            util::Log(logWARNING) << "Negative turn penalty at " << turn_index.from_id << ", "
 | 
			
		||||
                                  << turn_index.via_id << ", " << turn_index.to_id
 | 
			
		||||
                                  << ": turn penalty " << turn_weight_penalty;
 | 
			
		||||
            util::Log(logWARNING) << "Negative turn penalty at " << osm_turn.from << ", "
 | 
			
		||||
                                  << osm_turn.via << ", " << osm_turn.to << ": turn penalty "
 | 
			
		||||
                                  << turn_weight_penalty;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return updated_turns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IsRestrictionValid(const Timezoner &tz_handler,
 | 
			
		||||
                        const extractor::TurnRestriction &turn,
 | 
			
		||||
                        const std::vector<util::Coordinate> &coordinates,
 | 
			
		||||
                        const extractor::PackedOSMIDs &osm_node_ids)
 | 
			
		||||
{
 | 
			
		||||
    const auto via_node = osm_node_ids[turn.via.node];
 | 
			
		||||
    const auto from_node = osm_node_ids[turn.from.node];
 | 
			
		||||
    const auto to_node = osm_node_ids[turn.to.node];
 | 
			
		||||
    if (turn.condition.empty())
 | 
			
		||||
    {
 | 
			
		||||
        osrm::util::Log(logWARNING) << "Condition parsing failed for the turn " << from_node
 | 
			
		||||
                                    << " -> " << via_node << " -> " << to_node;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto lon = static_cast<double>(toFloating(coordinates[turn.via.node].lon));
 | 
			
		||||
    const auto lat = static_cast<double>(toFloating(coordinates[turn.via.node].lat));
 | 
			
		||||
    const auto &condition = turn.condition;
 | 
			
		||||
 | 
			
		||||
    // Get local time of the restriction
 | 
			
		||||
    const auto &local_time = tz_handler(point_t{lon, lat});
 | 
			
		||||
 | 
			
		||||
    // TODO: check restriction type [:<transportation mode>][:<direction>]
 | 
			
		||||
    // http://wiki.openstreetmap.org/wiki/Conditional_restrictions#Tagging
 | 
			
		||||
 | 
			
		||||
    // TODO: parsing will fail for combined conditions, e.g. Sa-Su AND weight>7
 | 
			
		||||
    // http://wiki.openstreetmap.org/wiki/Conditional_restrictions#Combined_conditions:_AND
 | 
			
		||||
 | 
			
		||||
    if (osrm::util::CheckOpeningHours(condition, local_time))
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::uint64_t>
 | 
			
		||||
updateConditionalTurns(const UpdaterConfig &config,
 | 
			
		||||
                       std::vector<TurnPenalty> &turn_weight_penalties,
 | 
			
		||||
                       const std::vector<extractor::TurnRestriction> &conditional_turns,
 | 
			
		||||
                       std::vector<util::Coordinate> &coordinates,
 | 
			
		||||
                       extractor::PackedOSMIDs &osm_node_ids,
 | 
			
		||||
                       Timezoner time_zone_handler)
 | 
			
		||||
{
 | 
			
		||||
    const auto turn_index_region =
 | 
			
		||||
        mmapFile(config.turn_penalties_index_path, boost::interprocess::read_only);
 | 
			
		||||
    // Mapped file pointer for turn indices
 | 
			
		||||
    const extractor::lookup::TurnIndexBlock *turn_index_blocks =
 | 
			
		||||
        reinterpret_cast<const extractor::lookup::TurnIndexBlock *>(
 | 
			
		||||
            turn_index_region.get_address());
 | 
			
		||||
    BOOST_ASSERT(is_aligned<extractor::lookup::TurnIndexBlock>(turn_index_blocks));
 | 
			
		||||
 | 
			
		||||
    std::vector<std::uint64_t> updated_turns;
 | 
			
		||||
    if (conditional_turns.size() == 0)
 | 
			
		||||
        return updated_turns;
 | 
			
		||||
 | 
			
		||||
    // TODO make this into a function
 | 
			
		||||
    LookupTable<std::tuple<NodeID, NodeID>, NodeID> is_only_lookup;
 | 
			
		||||
    std::unordered_set<std::tuple<NodeID, NodeID, NodeID>,
 | 
			
		||||
                       std::hash<std::tuple<NodeID, NodeID, NodeID>>>
 | 
			
		||||
        is_no_set;
 | 
			
		||||
    for (const auto &c : conditional_turns)
 | 
			
		||||
    {
 | 
			
		||||
        // only add restrictions to the lookups if the restriction is valid now
 | 
			
		||||
        if (!IsRestrictionValid(time_zone_handler, c, coordinates, osm_node_ids))
 | 
			
		||||
            continue;
 | 
			
		||||
        if (c.flags.is_only)
 | 
			
		||||
        {
 | 
			
		||||
            is_only_lookup.lookup.push_back({std::make_tuple(c.from.node, c.via.node), c.to.node});
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            is_no_set.insert({std::make_tuple(c.from.node, c.via.node, c.to.node)});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (std::uint64_t edge_index = 0; edge_index < turn_weight_penalties.size(); ++edge_index)
 | 
			
		||||
    {
 | 
			
		||||
        const extractor::lookup::TurnIndexBlock internal_turn = turn_index_blocks[edge_index];
 | 
			
		||||
 | 
			
		||||
        const auto is_no_tuple =
 | 
			
		||||
            std::make_tuple(internal_turn.from_id, internal_turn.via_id, internal_turn.to_id);
 | 
			
		||||
        const auto is_only_tuple = std::make_tuple(internal_turn.from_id, internal_turn.via_id);
 | 
			
		||||
        // turn has a no_* restriction
 | 
			
		||||
        if (is_no_set.find(is_no_tuple) != is_no_set.end())
 | 
			
		||||
        {
 | 
			
		||||
            util::Log(logDEBUG) << "Conditional penalty set on edge: " << edge_index;
 | 
			
		||||
            turn_weight_penalties[edge_index] = INVALID_TURN_PENALTY;
 | 
			
		||||
            updated_turns.push_back(edge_index);
 | 
			
		||||
        }
 | 
			
		||||
        // turn has an only_* restriction
 | 
			
		||||
        else if (is_only_lookup(is_only_tuple))
 | 
			
		||||
        {
 | 
			
		||||
            // with only_* restrictions, the turn on which the restriction is tagged is valid
 | 
			
		||||
            if (*is_only_lookup(is_only_tuple) == internal_turn.to_id)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            util::Log(logDEBUG) << "Conditional penalty set on edge: " << edge_index;
 | 
			
		||||
            turn_weight_penalties[edge_index] = INVALID_TURN_PENALTY;
 | 
			
		||||
            updated_turns.push_back(edge_index);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -425,8 +557,8 @@ updateTurnPenalties(const UpdaterConfig &config,
 | 
			
		||||
 | 
			
		||||
Updater::NumNodesAndEdges Updater::LoadAndUpdateEdgeExpandedGraph() const
 | 
			
		||||
{
 | 
			
		||||
    std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
 | 
			
		||||
    std::vector<EdgeWeight> node_weights;
 | 
			
		||||
    std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
 | 
			
		||||
    auto max_edge_id = Updater::LoadAndUpdateEdgeExpandedGraph(edge_based_edge_list, node_weights);
 | 
			
		||||
    return std::make_tuple(max_edge_id + 1, std::move(edge_based_edge_list));
 | 
			
		||||
}
 | 
			
		||||
@ -438,6 +570,8 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
 | 
			
		||||
    TIMER_START(load_edges);
 | 
			
		||||
 | 
			
		||||
    EdgeID max_edge_id = 0;
 | 
			
		||||
    std::vector<util::Coordinate> node_coordinates;
 | 
			
		||||
    extractor::PackedOSMIDs osm_node_ids;
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        storage::io::FileReader reader(config.edge_based_graph_path,
 | 
			
		||||
@ -446,12 +580,16 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
 | 
			
		||||
        edge_based_edge_list.resize(num_edges);
 | 
			
		||||
        max_edge_id = reader.ReadOne<EdgeID>();
 | 
			
		||||
        reader.ReadInto(edge_based_edge_list);
 | 
			
		||||
 | 
			
		||||
        extractor::files::readNodes(config.node_based_graph_path, node_coordinates, osm_node_ids);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const bool update_conditional_turns =
 | 
			
		||||
        !config.turn_restrictions_path.empty() && config.valid_now;
 | 
			
		||||
    const bool update_edge_weights = !config.segment_speed_lookup_paths.empty();
 | 
			
		||||
    const bool update_turn_penalties = !config.turn_penalty_lookup_paths.empty();
 | 
			
		||||
 | 
			
		||||
    if (!update_edge_weights && !update_turn_penalties)
 | 
			
		||||
    if (!update_edge_weights && !update_turn_penalties && !update_conditional_turns)
 | 
			
		||||
    {
 | 
			
		||||
        saveDatasourcesNames(config);
 | 
			
		||||
        return max_edge_id;
 | 
			
		||||
@ -467,7 +605,7 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
 | 
			
		||||
    extractor::ProfileProperties profile_properties;
 | 
			
		||||
    std::vector<TurnPenalty> turn_weight_penalties;
 | 
			
		||||
    std::vector<TurnPenalty> turn_duration_penalties;
 | 
			
		||||
    if (update_edge_weights || update_turn_penalties)
 | 
			
		||||
    if (update_edge_weights || update_turn_penalties || update_conditional_turns)
 | 
			
		||||
    {
 | 
			
		||||
        const auto load_segment_data = [&] {
 | 
			
		||||
            extractor::files::readSegmentData(config.geometry_path, segment_data);
 | 
			
		||||
@ -508,28 +646,70 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
 | 
			
		||||
                             load_profile_properties);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<extractor::TurnRestriction> conditional_turns;
 | 
			
		||||
    if (update_conditional_turns)
 | 
			
		||||
    {
 | 
			
		||||
        using storage::io::FileReader;
 | 
			
		||||
        FileReader reader(config.turn_restrictions_path, FileReader::VerifyFingerprint);
 | 
			
		||||
        extractor::serialization::read(reader, conditional_turns);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tbb::concurrent_vector<GeometryID> updated_segments;
 | 
			
		||||
    if (update_edge_weights)
 | 
			
		||||
    {
 | 
			
		||||
        auto segment_speed_lookup = csv::readSegmentValues(config.segment_speed_lookup_paths);
 | 
			
		||||
 | 
			
		||||
        TIMER_START(segment);
 | 
			
		||||
        updated_segments =
 | 
			
		||||
            updateSegmentData(config, profile_properties, segment_speed_lookup, segment_data);
 | 
			
		||||
        updated_segments = updateSegmentData(config,
 | 
			
		||||
                                             profile_properties,
 | 
			
		||||
                                             segment_speed_lookup,
 | 
			
		||||
                                             segment_data,
 | 
			
		||||
                                             node_coordinates,
 | 
			
		||||
                                             osm_node_ids);
 | 
			
		||||
        // Now save out the updated compressed geometries
 | 
			
		||||
        extractor::files::writeSegmentData(config.geometry_path, segment_data);
 | 
			
		||||
        TIMER_STOP(segment);
 | 
			
		||||
        util::Log() << "Updating segment data took " << TIMER_MSEC(segment) << "ms.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto turn_penalty_lookup = csv::readTurnValues(config.turn_penalty_lookup_paths);
 | 
			
		||||
    if (update_turn_penalties)
 | 
			
		||||
    {
 | 
			
		||||
        auto turn_penalty_lookup = csv::readTurnValues(config.turn_penalty_lookup_paths);
 | 
			
		||||
        auto updated_turn_penalties = updateTurnPenalties(config,
 | 
			
		||||
                                                          profile_properties,
 | 
			
		||||
                                                          turn_penalty_lookup,
 | 
			
		||||
                                                          turn_weight_penalties,
 | 
			
		||||
                                                          turn_duration_penalties);
 | 
			
		||||
                                                          turn_duration_penalties,
 | 
			
		||||
                                                          osm_node_ids);
 | 
			
		||||
        const auto offset = updated_segments.size();
 | 
			
		||||
        updated_segments.resize(offset + updated_turn_penalties.size());
 | 
			
		||||
        // we need to re-compute all edges that have updated turn penalties.
 | 
			
		||||
        // this marks it for re-computation
 | 
			
		||||
        std::transform(updated_turn_penalties.begin(),
 | 
			
		||||
                       updated_turn_penalties.end(),
 | 
			
		||||
                       updated_segments.begin() + offset,
 | 
			
		||||
                       [&node_data, &edge_based_edge_list](const std::uint64_t turn_id) {
 | 
			
		||||
                           const auto node_id = edge_based_edge_list[turn_id].source;
 | 
			
		||||
                           return node_data.GetGeometryID(node_id);
 | 
			
		||||
                       });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (update_conditional_turns)
 | 
			
		||||
    {
 | 
			
		||||
        // initialize instance of class that handles time zone resolution
 | 
			
		||||
        if (config.valid_now <= 0)
 | 
			
		||||
        {
 | 
			
		||||
            util::Log(logERROR) << "Given UTC time is invalid: " << config.valid_now;
 | 
			
		||||
            throw;
 | 
			
		||||
        }
 | 
			
		||||
        const Timezoner time_zone_handler = Timezoner(config.tz_file_path, config.valid_now);
 | 
			
		||||
 | 
			
		||||
        auto updated_turn_penalties = updateConditionalTurns(config,
 | 
			
		||||
                                                             turn_weight_penalties,
 | 
			
		||||
                                                             conditional_turns,
 | 
			
		||||
                                                             node_coordinates,
 | 
			
		||||
                                                             osm_node_ids,
 | 
			
		||||
                                                             time_zone_handler);
 | 
			
		||||
        const auto offset = updated_segments.size();
 | 
			
		||||
        updated_segments.resize(offset + updated_turn_penalties.size());
 | 
			
		||||
        // we need to re-compute all edges that have updated turn penalties.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										96
									
								
								src/util/conditional_restrictions.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/util/conditional_restrictions.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
			
		||||
#include "util/conditional_restrictions.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/fusion/include/adapt_struct.hpp>
 | 
			
		||||
#include <boost/spirit/include/phoenix.hpp>
 | 
			
		||||
#include <boost/spirit/include/qi.hpp>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
// Debug output stream operators for use with BOOST_SPIRIT_DEBUG
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const ConditionalRestriction &restriction)
 | 
			
		||||
{
 | 
			
		||||
    return stream << restriction.value << "=" << restriction.condition;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BOOST_FUSION_ADAPT_STRUCT(osrm::util::ConditionalRestriction,
 | 
			
		||||
                          (std::string, value)(std::string, condition))
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
namespace detail
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
namespace ph = boost::phoenix;
 | 
			
		||||
namespace qi = boost::spirit::qi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Iterator, typename Skipper = qi::blank_type>
 | 
			
		||||
struct conditional_restrictions_grammar
 | 
			
		||||
    : qi::grammar<Iterator, Skipper, std::vector<ConditionalRestriction>()>
 | 
			
		||||
{
 | 
			
		||||
    // http://wiki.openstreetmap.org/wiki/Conditional_restrictions
 | 
			
		||||
    conditional_restrictions_grammar() : conditional_restrictions_grammar::base_type(restrictions)
 | 
			
		||||
    {
 | 
			
		||||
        using qi::_1;
 | 
			
		||||
        using qi::_val;
 | 
			
		||||
        using qi::lit;
 | 
			
		||||
 | 
			
		||||
        // clang-format off
 | 
			
		||||
 | 
			
		||||
        restrictions
 | 
			
		||||
            = restriction % ';'
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        restriction
 | 
			
		||||
            = value >> '@' >> condition
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        value
 | 
			
		||||
            = +(qi::char_ - '@')
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        condition
 | 
			
		||||
            = *qi::blank
 | 
			
		||||
            >> (lit('(') >> qi::as_string[qi::no_skip[*~lit(')')]][_val = _1] >> lit(')')
 | 
			
		||||
                | qi::as_string[qi::no_skip[*~lit(';')]][_val = _1]
 | 
			
		||||
               )
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        // clang-format on
 | 
			
		||||
 | 
			
		||||
        BOOST_SPIRIT_DEBUG_NODES((restrictions)(restriction)(value)(condition));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, std::vector<ConditionalRestriction>()> restrictions;
 | 
			
		||||
    qi::rule<Iterator, Skipper, ConditionalRestriction()> restriction;
 | 
			
		||||
    qi::rule<Iterator, Skipper, std::string()> value, condition;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<ConditionalRestriction> ParseConditionalRestrictions(const std::string &str)
 | 
			
		||||
{
 | 
			
		||||
    auto it(str.begin()), end(str.end());
 | 
			
		||||
    const detail::conditional_restrictions_grammar<decltype(it)> static grammar;
 | 
			
		||||
 | 
			
		||||
    std::vector<ConditionalRestriction> result;
 | 
			
		||||
    bool ok = boost::spirit::qi::phrase_parse(it, end, grammar, boost::spirit::qi::blank, result);
 | 
			
		||||
 | 
			
		||||
    if (!ok || it != end)
 | 
			
		||||
        return std::vector<ConditionalRestriction>();
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // util
 | 
			
		||||
} // osrm
 | 
			
		||||
							
								
								
									
										435
									
								
								src/util/opening_hours.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								src/util/opening_hours.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,435 @@
 | 
			
		||||
#include "util/opening_hours.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/spirit/include/phoenix.hpp>
 | 
			
		||||
#include <boost/spirit/include/qi.hpp>
 | 
			
		||||
 | 
			
		||||
#include <boost/io/ios_state.hpp>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cctype>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace util
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
// Debug output stream operators for use with BOOST_SPIRIT_DEBUG
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Modifier value)
 | 
			
		||||
{
 | 
			
		||||
    switch (value)
 | 
			
		||||
    {
 | 
			
		||||
    case OpeningHours::unknown:
 | 
			
		||||
        return stream << "unknown";
 | 
			
		||||
    case OpeningHours::open:
 | 
			
		||||
        return stream << "open";
 | 
			
		||||
    case OpeningHours::closed:
 | 
			
		||||
        return stream << "closed";
 | 
			
		||||
    case OpeningHours::off:
 | 
			
		||||
        return stream << "off";
 | 
			
		||||
    case OpeningHours::is24_7:
 | 
			
		||||
        return stream << "24/7";
 | 
			
		||||
    }
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Time::Event value)
 | 
			
		||||
{
 | 
			
		||||
    switch (value)
 | 
			
		||||
    {
 | 
			
		||||
    case OpeningHours::Time::dawn:
 | 
			
		||||
        return stream << "dawn";
 | 
			
		||||
    case OpeningHours::Time::sunrise:
 | 
			
		||||
        return stream << "sunrise";
 | 
			
		||||
    case OpeningHours::Time::sunset:
 | 
			
		||||
        return stream << "sunset";
 | 
			
		||||
    case OpeningHours::Time::dusk:
 | 
			
		||||
        return stream << "dusk";
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Time &value)
 | 
			
		||||
{
 | 
			
		||||
    boost::io::ios_flags_saver ifs(stream);
 | 
			
		||||
    if (value.event == OpeningHours::Time::invalid)
 | 
			
		||||
        return stream << "???";
 | 
			
		||||
    if (value.event == OpeningHours::Time::none)
 | 
			
		||||
        return stream << std::setfill('0') << std::setw(2) << value.minutes / 60 << ":"
 | 
			
		||||
                      << std::setfill('0') << std::setw(2) << value.minutes % 60;
 | 
			
		||||
    stream << value.event;
 | 
			
		||||
    if (value.minutes != 0)
 | 
			
		||||
        stream << value.minutes;
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::TimeSpan &value)
 | 
			
		||||
{
 | 
			
		||||
    return stream << value.from << "-" << value.to;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::Monthday &value)
 | 
			
		||||
{
 | 
			
		||||
    bool empty = true;
 | 
			
		||||
    if (value.year != 0)
 | 
			
		||||
    {
 | 
			
		||||
        stream << (int)value.year;
 | 
			
		||||
        empty = false;
 | 
			
		||||
    };
 | 
			
		||||
    if (value.month != 0)
 | 
			
		||||
    {
 | 
			
		||||
        stream << (empty ? "" : "/") << (int)value.month;
 | 
			
		||||
        empty = false;
 | 
			
		||||
    };
 | 
			
		||||
    if (value.day != 0)
 | 
			
		||||
    {
 | 
			
		||||
        stream << (empty ? "" : "/") << (int)value.day;
 | 
			
		||||
    };
 | 
			
		||||
    return stream;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::WeekdayRange &value)
 | 
			
		||||
{
 | 
			
		||||
    boost::io::ios_flags_saver ifs(stream);
 | 
			
		||||
    return stream << std::hex << std::setfill('0') << std::setw(2) << value.weekdays;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours::MonthdayRange &value)
 | 
			
		||||
{
 | 
			
		||||
    return stream << value.from << "-" << value.to;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline std::ostream &operator<<(std::ostream &stream, const OpeningHours &value)
 | 
			
		||||
{
 | 
			
		||||
    if (value.modifier == OpeningHours::is24_7)
 | 
			
		||||
        return stream << OpeningHours::is24_7;
 | 
			
		||||
 | 
			
		||||
    for (auto x : value.monthdays)
 | 
			
		||||
        stream << x << ", ";
 | 
			
		||||
    for (auto x : value.weekdays)
 | 
			
		||||
        stream << x << ", ";
 | 
			
		||||
    for (auto x : value.times)
 | 
			
		||||
        stream << x << ", ";
 | 
			
		||||
    return stream << " |" << value.modifier << "|";
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace detail
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
namespace ph = boost::phoenix;
 | 
			
		||||
namespace qi = boost::spirit::qi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Iterator, typename Skipper = qi::blank_type>
 | 
			
		||||
struct opening_hours_grammar : qi::grammar<Iterator, Skipper, std::vector<OpeningHours>()>
 | 
			
		||||
{
 | 
			
		||||
    // http://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
 | 
			
		||||
    opening_hours_grammar() : opening_hours_grammar::base_type(time_domain)
 | 
			
		||||
    {
 | 
			
		||||
        using qi::_1;
 | 
			
		||||
        using qi::_a;
 | 
			
		||||
        using qi::_b;
 | 
			
		||||
        using qi::_c;
 | 
			
		||||
        using qi::_r1;
 | 
			
		||||
        using qi::_pass;
 | 
			
		||||
        using qi::_val;
 | 
			
		||||
        using qi::eoi;
 | 
			
		||||
        using qi::lit;
 | 
			
		||||
        using qi::char_;
 | 
			
		||||
        using qi::uint_;
 | 
			
		||||
        using oh = osrm::util::OpeningHours;
 | 
			
		||||
 | 
			
		||||
        // clang-format off
 | 
			
		||||
 | 
			
		||||
        // General syntax
 | 
			
		||||
        time_domain = rule_sequence[ph::push_back(_val, _1)] % any_rule_separator;
 | 
			
		||||
 | 
			
		||||
        rule_sequence
 | 
			
		||||
            = lit("24/7")[ph::bind(&oh::modifier, _val) = oh::is24_7]
 | 
			
		||||
            | (selector_sequence[_val = _1] >> -rule_modifier[ph::bind(&oh::modifier, _val) = _1] >> -comment)
 | 
			
		||||
            | comment
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        any_rule_separator = char_(';') | lit("||") | additional_rule_separator;
 | 
			
		||||
 | 
			
		||||
        additional_rule_separator = char_(',');
 | 
			
		||||
 | 
			
		||||
        // Rule modifiers
 | 
			
		||||
        rule_modifier.add
 | 
			
		||||
            ("unknown", oh::unknown)
 | 
			
		||||
            ("open", oh::open)
 | 
			
		||||
            ("closed", oh::closed)
 | 
			
		||||
            ("off", oh::off)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        // Selectors
 | 
			
		||||
        selector_sequence = (wide_range_selectors(_a) >> small_range_selectors(_a))[_val = _a];
 | 
			
		||||
 | 
			
		||||
        wide_range_selectors
 | 
			
		||||
            = (-monthday_selector(_r1)
 | 
			
		||||
               >> -year_selector(_r1)
 | 
			
		||||
               >> -week_selector(_r1) // TODO week_selector
 | 
			
		||||
              ) >> -lit(':')
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        small_range_selectors = -(weekday_selector(_r1) >> (&~lit(',') | eoi)) >> -time_selector(_r1);
 | 
			
		||||
 | 
			
		||||
        // Time selector
 | 
			
		||||
        time_selector = (timespan % ',')[ph::bind(&OpeningHours::times, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        timespan
 | 
			
		||||
            = (time[_a = _1]
 | 
			
		||||
               >> -(lit('+')[_b = ph::construct<OpeningHours::Time>(24, 0)]
 | 
			
		||||
                    | ('-' >> extended_time[_b = _1]
 | 
			
		||||
                       >> -('+' | '/' >> (minute | hour_minutes))))
 | 
			
		||||
               )[_val = ph::construct<OpeningHours::TimeSpan>(_a, _b)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        time = hour_minutes | variable_time;
 | 
			
		||||
 | 
			
		||||
        extended_time = extended_hour_minutes | variable_time;
 | 
			
		||||
 | 
			
		||||
        variable_time
 | 
			
		||||
            = event[_val = ph::construct<OpeningHours::Time>(_1)]
 | 
			
		||||
            | ('(' >> event[_a = _1] >> plus_or_minus[_b = _1] >> hour_minutes[_c = _1] >> ')')
 | 
			
		||||
            [_val = ph::construct<OpeningHours::Time>(_a, _b, _c)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        event.add
 | 
			
		||||
            ("dawn", OpeningHours::Time::dawn)
 | 
			
		||||
            ("sunrise", OpeningHours::Time::sunrise)
 | 
			
		||||
            ("sunset", OpeningHours::Time::sunset)
 | 
			
		||||
            ("dusk", OpeningHours::Time::dusk)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        // Weekday selector
 | 
			
		||||
        weekday_selector
 | 
			
		||||
            = (holiday_sequence(_r1) >> -(char_(", ") >> weekday_sequence(_r1)))
 | 
			
		||||
            | (weekday_sequence(_r1) >> -(char_(", ") >> holiday_sequence(_r1)))
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        weekday_sequence = (weekday_range % ',')[ph::bind(&OpeningHours::weekdays, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        weekday_range
 | 
			
		||||
            = wday[_a = _1, _b = _1]
 | 
			
		||||
            >> -(('-' >> wday[_b = _1])
 | 
			
		||||
                 | ('[' >> (nth_entry % ',') >> ']' >> -day_offset))
 | 
			
		||||
            [_val = ph::construct<OpeningHours::WeekdayRange>(_a, _b)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        holiday_sequence = (lit("SH") >> -day_offset) | lit("PH");
 | 
			
		||||
 | 
			
		||||
        nth_entry = nth | nth >> '-' >> nth | '-' >> nth;
 | 
			
		||||
 | 
			
		||||
        nth = char_("12345");
 | 
			
		||||
 | 
			
		||||
        day_offset = plus_or_minus >> uint_ >> lit("days");
 | 
			
		||||
 | 
			
		||||
        // Week selector
 | 
			
		||||
        week_selector = (lit("week ") >> week) % ',';
 | 
			
		||||
 | 
			
		||||
        week = weeknum >> -('-' >> weeknum >> -('/' >> uint_));
 | 
			
		||||
 | 
			
		||||
        // Month selector
 | 
			
		||||
        monthday_selector = (monthday_range % ',')[ph::bind(&OpeningHours::monthdays, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        monthday_range
 | 
			
		||||
            = (date_from[ph::bind(&OpeningHours::MonthdayRange::from, _val) = _1]
 | 
			
		||||
               >> -date_offset
 | 
			
		||||
               >> '-'
 | 
			
		||||
               >> date_to[ph::bind(&OpeningHours::MonthdayRange::to, _val) = _1]
 | 
			
		||||
               >> -date_offset)
 | 
			
		||||
            | (date_from[ph::bind(&OpeningHours::MonthdayRange::from, _val) = _1]
 | 
			
		||||
               >> -(date_offset
 | 
			
		||||
                    >> -lit('+')[ph::bind(&OpeningHours::MonthdayRange::from, _val) = ph::construct<OpeningHours::Monthday>(-1)]
 | 
			
		||||
                    ))
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        date_offset = (plus_or_minus >> wday) | day_offset;
 | 
			
		||||
 | 
			
		||||
        date_from
 | 
			
		||||
            = ((-year[_a = _1] >> ((month[_b = _1] >> -daynum[_c = _1]) | daynum[_c = _1]))
 | 
			
		||||
               | variable_date)
 | 
			
		||||
            [_val = ph::construct<OpeningHours::Monthday>(_a, _b, _c)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        date_to
 | 
			
		||||
            = date_from[_val = _1]
 | 
			
		||||
            | daynum[_val = ph::construct<OpeningHours::Monthday>(0, 0, _1)]
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        variable_date = lit("easter");
 | 
			
		||||
 | 
			
		||||
        // Year selector
 | 
			
		||||
        year_selector = (year_range % ',')[ph::bind(&OpeningHours::monthdays, _r1) = _1];
 | 
			
		||||
 | 
			
		||||
        year_range
 | 
			
		||||
            = year[ph::bind(&OpeningHours::MonthdayRange::from, _val) = ph::construct<OpeningHours::Monthday>(_1)]
 | 
			
		||||
            >> -(('-' >> year[ph::bind(&OpeningHours::MonthdayRange::to, _val) = ph::construct<OpeningHours::Monthday>(_1)]
 | 
			
		||||
                  >> -('/' >> uint_))
 | 
			
		||||
                 | lit('+')[ph::bind(&OpeningHours::MonthdayRange::to, _val) = ph::construct<OpeningHours::Monthday>(-1)]);
 | 
			
		||||
 | 
			
		||||
        // Basic elements
 | 
			
		||||
        plus_or_minus = lit('+')[_val = true] | lit('-')[_val = false];
 | 
			
		||||
 | 
			
		||||
        hour = uint2_p[_pass = bind([](unsigned x) { return x <= 24; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        extended_hour = uint2_p[_pass = bind([](unsigned x) { return x <= 48; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        minute = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        hour_minutes =
 | 
			
		||||
            hour[_a = _1] >> ':' >> minute[_val = ph::construct<OpeningHours::Time>(_a, _1)];
 | 
			
		||||
 | 
			
		||||
        extended_hour_minutes = extended_hour[_a = _1] >> ':' >>
 | 
			
		||||
                                minute[_val = ph::construct<OpeningHours::Time>(_a, _1)];
 | 
			
		||||
 | 
			
		||||
        wday.add
 | 
			
		||||
            ("Su", 0)
 | 
			
		||||
            ("Mo", 1)
 | 
			
		||||
            ("Tu", 2)
 | 
			
		||||
            ("We", 3)
 | 
			
		||||
            ("Th", 4)
 | 
			
		||||
            ("Fr", 5)
 | 
			
		||||
            ("Sa", 6)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        daynum
 | 
			
		||||
            = uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 31; }, _1), _val = _1]
 | 
			
		||||
            >> (&~lit(':') | eoi)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        weeknum = uint2_p[_pass = bind([](unsigned x) { return 01 <= x && x <= 53; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        month.add
 | 
			
		||||
            ("Jan", 1)
 | 
			
		||||
            ("Feb", 2)
 | 
			
		||||
            ("Mar", 3)
 | 
			
		||||
            ("Apr", 4)
 | 
			
		||||
            ("May", 5)
 | 
			
		||||
            ("Jun", 6)
 | 
			
		||||
            ("Jul", 7)
 | 
			
		||||
            ("Aug", 8)
 | 
			
		||||
            ("Sep", 9)
 | 
			
		||||
            ("Oct", 10)
 | 
			
		||||
            ("Nov", 11)
 | 
			
		||||
            ("Dec", 12)
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
        year = uint4_p[_pass = bind([](unsigned x) { return x > 1900; }, _1), _val = _1];
 | 
			
		||||
 | 
			
		||||
        comment = lit('"') >> *(~qi::char_('"')) >> lit('"');
 | 
			
		||||
 | 
			
		||||
        // clang-format on
 | 
			
		||||
 | 
			
		||||
        BOOST_SPIRIT_DEBUG_NODES((time_domain)(rule_sequence)(any_rule_separator)(
 | 
			
		||||
            selector_sequence)(wide_range_selectors)(small_range_selectors)(time_selector)(
 | 
			
		||||
            timespan)(time)(extended_time)(variable_time)(weekday_selector)(weekday_sequence)(
 | 
			
		||||
            weekday_range)(holiday_sequence)(nth_entry)(nth)(day_offset)(week_selector)(week)(
 | 
			
		||||
            monthday_selector)(monthday_range)(date_offset)(date_from)(date_to)(variable_date)(
 | 
			
		||||
            year_selector)(year_range)(plus_or_minus)(hour_minutes)(extended_hour_minutes)(comment)(
 | 
			
		||||
            hour)(extended_hour)(minute)(daynum)(weeknum)(year));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, std::vector<OpeningHours>()> time_domain;
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours()> rule_sequence;
 | 
			
		||||
    qi::rule<Iterator, Skipper, void()> any_rule_separator, additional_rule_separator;
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours(), qi::locals<OpeningHours>> selector_sequence;
 | 
			
		||||
    qi::symbols<char const, OpeningHours::Modifier> rule_modifier;
 | 
			
		||||
    qi::rule<Iterator, Skipper, void(OpeningHours &)> wide_range_selectors, small_range_selectors,
 | 
			
		||||
        time_selector, weekday_selector, year_selector, monthday_selector, week_selector;
 | 
			
		||||
 | 
			
		||||
    // Time rules
 | 
			
		||||
    qi::rule<Iterator,
 | 
			
		||||
             Skipper,
 | 
			
		||||
             OpeningHours::TimeSpan(),
 | 
			
		||||
             qi::locals<OpeningHours::Time, OpeningHours::Time>>
 | 
			
		||||
        timespan;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Time()> time, extended_time;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator,
 | 
			
		||||
             Skipper,
 | 
			
		||||
             OpeningHours::Time(),
 | 
			
		||||
             qi::locals<OpeningHours::Time::Event, bool, OpeningHours::Time>>
 | 
			
		||||
        variable_time;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Time(), qi::locals<unsigned>> hour_minutes,
 | 
			
		||||
        extended_hour_minutes;
 | 
			
		||||
 | 
			
		||||
    qi::symbols<char const, OpeningHours::Time::Event> event;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, bool()> plus_or_minus;
 | 
			
		||||
 | 
			
		||||
    // Weekday rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, void(OpeningHours &)> weekday_sequence, holiday_sequence;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator,
 | 
			
		||||
             Skipper,
 | 
			
		||||
             OpeningHours::WeekdayRange(),
 | 
			
		||||
             qi::locals<unsigned char, unsigned char>>
 | 
			
		||||
        weekday_range;
 | 
			
		||||
 | 
			
		||||
    // Monthday rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::MonthdayRange()> monthday_range;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Monthday(), qi::locals<unsigned, unsigned, unsigned>>
 | 
			
		||||
        date_from;
 | 
			
		||||
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::Monthday()> date_to;
 | 
			
		||||
 | 
			
		||||
    // Year rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, OpeningHours::MonthdayRange()> year_range;
 | 
			
		||||
 | 
			
		||||
    // Unused rules
 | 
			
		||||
    qi::rule<Iterator, Skipper, void()> nth_entry, nth, day_offset, week, date_offset,
 | 
			
		||||
        variable_date, comment;
 | 
			
		||||
 | 
			
		||||
    // Basic rules and parsers
 | 
			
		||||
    qi::rule<Iterator, Skipper, unsigned()> hour, extended_hour, minute, daynum, weeknum, year;
 | 
			
		||||
    qi::symbols<char const, unsigned char> wday, month;
 | 
			
		||||
    qi::uint_parser<unsigned, 10, 2, 2> uint2_p;
 | 
			
		||||
    qi::uint_parser<unsigned, 10, 4, 4> uint4_p;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<OpeningHours> ParseOpeningHours(const std::string &str)
 | 
			
		||||
{
 | 
			
		||||
    auto it(str.begin()), end(str.end());
 | 
			
		||||
    const detail::opening_hours_grammar<decltype(it)> static grammar;
 | 
			
		||||
 | 
			
		||||
    std::vector<OpeningHours> result;
 | 
			
		||||
    bool ok = boost::spirit::qi::phrase_parse(it, end, grammar, boost::spirit::qi::blank, result);
 | 
			
		||||
 | 
			
		||||
    if (!ok || it != end)
 | 
			
		||||
        return std::vector<OpeningHours>();
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CheckOpeningHours(const std::vector<OpeningHours> &input, const struct tm &time)
 | 
			
		||||
{
 | 
			
		||||
    bool is_open = false;
 | 
			
		||||
    for (auto &opening_hours : input)
 | 
			
		||||
    {
 | 
			
		||||
        if (opening_hours.modifier == OpeningHours::is24_7)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (opening_hours.IsInRange(time))
 | 
			
		||||
        {
 | 
			
		||||
            is_open = opening_hours.modifier == OpeningHours::open;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return is_open;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // util
 | 
			
		||||
} // osrm
 | 
			
		||||
							
								
								
									
										140
									
								
								src/util/timezones.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/util/timezones.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,140 @@
 | 
			
		||||
#include "util/timezones.hpp"
 | 
			
		||||
#include "util/exception.hpp"
 | 
			
		||||
#include "util/log.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/scope_exit.hpp>
 | 
			
		||||
 | 
			
		||||
#ifdef ENABLE_SHAPEFILE
 | 
			
		||||
#include <shapefil.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
// Function loads time zone shape polygons, computes a zone local time for utc_time,
 | 
			
		||||
// creates a lookup R-tree and returns a lambda function that maps a point
 | 
			
		||||
// to the corresponding local time
 | 
			
		||||
namespace osrm
 | 
			
		||||
{
 | 
			
		||||
namespace updater
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
bool SupportsShapefiles()
 | 
			
		||||
{
 | 
			
		||||
#ifdef ENABLE_SHAPEFILE
 | 
			
		||||
    return true;
 | 
			
		||||
#else
 | 
			
		||||
    return false;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Timezoner::Timezoner(std::string tz_filename, std::time_t utc_time_now)
 | 
			
		||||
{
 | 
			
		||||
    util::Log() << "Time zone validation based on UTC time : " << utc_time_now;
 | 
			
		||||
    // Thread safety: MT-Unsafe const:env
 | 
			
		||||
    default_time = *gmtime(&utc_time_now);
 | 
			
		||||
    LoadLocalTimesRTree(tz_filename, utc_time_now);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Timezoner::LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time)
 | 
			
		||||
{
 | 
			
		||||
    if (tz_shapes_filename.empty())
 | 
			
		||||
        return;
 | 
			
		||||
#ifdef ENABLE_SHAPEFILE
 | 
			
		||||
    // Load time zones shapes and collect local times of utc_time
 | 
			
		||||
    auto shphandle = SHPOpen(tz_shapes_filename.c_str(), "rb");
 | 
			
		||||
    auto dbfhandle = DBFOpen(tz_shapes_filename.c_str(), "rb");
 | 
			
		||||
 | 
			
		||||
    BOOST_SCOPE_EXIT(&shphandle, &dbfhandle)
 | 
			
		||||
    {
 | 
			
		||||
        DBFClose(dbfhandle);
 | 
			
		||||
        SHPClose(shphandle);
 | 
			
		||||
    }
 | 
			
		||||
    BOOST_SCOPE_EXIT_END
 | 
			
		||||
 | 
			
		||||
    if (!shphandle || !dbfhandle)
 | 
			
		||||
    {
 | 
			
		||||
        throw osrm::util::exception("failed to open " + tz_shapes_filename + ".shp or " +
 | 
			
		||||
                                    tz_shapes_filename + ".dbf file");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int num_entities, shape_type;
 | 
			
		||||
    SHPGetInfo(shphandle, &num_entities, &shape_type, NULL, NULL);
 | 
			
		||||
    if (num_entities != DBFGetRecordCount(dbfhandle))
 | 
			
		||||
    {
 | 
			
		||||
        throw osrm::util::exception("inconsistent " + tz_shapes_filename + ".shp and " +
 | 
			
		||||
                                    tz_shapes_filename + ".dbf files");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto tzid = DBFGetFieldIndex(dbfhandle, "TZID");
 | 
			
		||||
    if (tzid == -1)
 | 
			
		||||
    {
 | 
			
		||||
        throw osrm::util::exception("did not find field called 'TZID' in the " +
 | 
			
		||||
                                    tz_shapes_filename + ".dbf file");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Lambda function that returns local time in the tzname time zone
 | 
			
		||||
    // Thread safety: MT-Unsafe const:env
 | 
			
		||||
    std::unordered_map<std::string, struct tm> local_time_memo;
 | 
			
		||||
    auto get_local_time_in_tz = [utc_time, &local_time_memo](const char *tzname) {
 | 
			
		||||
        auto it = local_time_memo.find(tzname);
 | 
			
		||||
        if (it == local_time_memo.end())
 | 
			
		||||
        {
 | 
			
		||||
            struct tm timeinfo;
 | 
			
		||||
            setenv("TZ", tzname, 1);
 | 
			
		||||
            tzset();
 | 
			
		||||
            localtime_r(&utc_time, &timeinfo);
 | 
			
		||||
            it = local_time_memo.insert({tzname, timeinfo}).first;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return it->second;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Get all time zone shapes and save local times in a vector
 | 
			
		||||
    std::vector<rtree_t::value_type> polygons;
 | 
			
		||||
    for (int shape = 0; shape < num_entities; ++shape)
 | 
			
		||||
    {
 | 
			
		||||
        auto object = SHPReadObject(shphandle, shape);
 | 
			
		||||
        BOOST_SCOPE_EXIT(&object) { SHPDestroyObject(object); }
 | 
			
		||||
        BOOST_SCOPE_EXIT_END
 | 
			
		||||
 | 
			
		||||
        if (object && object->nSHPType == SHPT_POLYGON)
 | 
			
		||||
        {
 | 
			
		||||
            // Find time zone polygon and place its bbox in into R-Tree
 | 
			
		||||
            polygon_t polygon;
 | 
			
		||||
            for (int vertex = 0; vertex < object->nVertices; ++vertex)
 | 
			
		||||
            {
 | 
			
		||||
                polygon.outer().emplace_back(object->padfX[vertex], object->padfY[vertex]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            polygons.emplace_back(boost::geometry::return_envelope<box_t>(polygon),
 | 
			
		||||
                                  local_times.size());
 | 
			
		||||
 | 
			
		||||
            // Get time zone name and emplace polygon and local time for the UTC input
 | 
			
		||||
            const auto tzname = DBFReadStringAttribute(dbfhandle, shape, tzid);
 | 
			
		||||
            local_times.emplace_back(local_time_t{polygon, get_local_time_in_tz(tzname)});
 | 
			
		||||
 | 
			
		||||
            // std::cout << boost::geometry::dsv(boost::geometry::return_envelope<box_t>(polygon))
 | 
			
		||||
            //           << " " << tzname << " " << asctime(&local_times.back().second);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create R-tree for collected shape polygons
 | 
			
		||||
    rtree = rtree_t(polygons);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct tm Timezoner::operator()(const point_t &point) const
 | 
			
		||||
{
 | 
			
		||||
    std::vector<rtree_t::value_type> result;
 | 
			
		||||
    rtree.query(boost::geometry::index::intersects(point), std::back_inserter(result));
 | 
			
		||||
    for (const auto v : result)
 | 
			
		||||
    {
 | 
			
		||||
        const auto index = v.second;
 | 
			
		||||
        if (boost::geometry::within(point, local_times[index].first))
 | 
			
		||||
            return local_times[index].second;
 | 
			
		||||
    }
 | 
			
		||||
    return default_time;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								test/data/tz/OGRGeoJSON.dbf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/data/tz/OGRGeoJSON.dbf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								test/data/tz/OGRGeoJSON.prj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/data/tz/OGRGeoJSON.prj
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								test/data/tz/OGRGeoJSON.shp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/data/tz/OGRGeoJSON.shp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								test/data/tz/OGRGeoJSON.shx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/data/tz/OGRGeoJSON.shx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -102,15 +102,15 @@ target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 | 
			
		||||
target_include_directories(partition-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 | 
			
		||||
target_include_directories(customizer-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 | 
			
		||||
 | 
			
		||||
target_link_libraries(engine-tests ${ENGINE_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(partition-tests ${PARTITIONER_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(customizer-tests ${CUSTOMIZER_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(engine-tests ${ENGINE_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${MAYBE_SHAPEFILE})
 | 
			
		||||
target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${MAYBE_SHAPEFILE})
 | 
			
		||||
target_link_libraries(partition-tests ${PARTITIONER_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${MAYBE_SHAPEFILE})
 | 
			
		||||
target_link_libraries(customizer-tests ${CUSTOMIZER_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${MAYBE_SHAPEFILE})
 | 
			
		||||
target_link_libraries(library-tests osrm ${ENGINE_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(library-extract-tests osrm_extract ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(library-contract-tests osrm_contract ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(server-tests osrm ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(util-tests ${UTIL_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 | 
			
		||||
target_link_libraries(server-tests osrm ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${MAYBE_SHAPEFILE})
 | 
			
		||||
target_link_libraries(util-tests ${UTIL_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${MAYBE_SHAPEFILE})
 | 
			
		||||
 | 
			
		||||
add_custom_target(tests
 | 
			
		||||
	DEPENDS engine-tests extractor-tests partition-tests customizer-tests library-tests library-extract-tests server-tests util-tests)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user